<template>
  <div class="login-container">
    <!-- 背景层 -->
    <div class="login-bg">
      <!-- 城市建筑轮廓装饰 -->
      <div class="city-skyline">
        <svg class="city-svg" viewBox="0 0 1200 500" preserveAspectRatio="xMidYMid meet">
          <!-- 最左侧建筑群 -->
          <path d="M20,400 L50,400 L50,320 L60,320 L60,400 L80,400 L80,280 L100,280 L100,400 L120,400 L120,340 L140,340 L140,400 L160,400 L160,300 L180,300 L180,400 L200,400 L200,360 L220,360 L220,400 L20,400 Z" fill="none" stroke="rgba(255,255,255,0.25)" stroke-width="2"/>
          <!-- 左侧建筑群2 -->
          <path d="M240,400 L260,400 L260,250 L280,250 L280,400 L300,400 L300,290 L320,290 L320,400 L340,400 L340,270 L360,270 L360,400 L240,400 Z" fill="none" stroke="rgba(255,255,255,0.22)" stroke-width="2"/>
          <!-- 左侧中建筑 -->
          <path d="M380,400 L420,400 L420,180 L440,180 L440,400 L460,400 L460,200 L480,200 L480,400 L500,400 L500,160 L520,160 L520,400 L380,400 Z" fill="none" stroke="rgba(255,255,255,0.25)" stroke-width="2"/>
          <!-- 中间左侧建筑群 -->
          <path d="M540,400 L560,400 L560,220 L580,220 L580,400 L600,400 L600,140 L620,140 L620,400 L640,400 L640,180 L660,180 L660,400 L680,400 L680,200 L700,200 L700,400 L540,400 Z" fill="none" stroke="rgba(255,255,255,0.28)" stroke-width="2"/>
          <!-- 中间主建筑 -->
          <path d="M720,400 L740,400 L740,100 L760,100 L760,400 L780,400 L780,80 L800,80 L800,400 L820,400 L820,120 L840,120 L840,400 L720,400 Z" fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="2.5"/>
          <!-- 中间右侧建筑 -->
          <path d="M860,400 L880,400 L880,150 L900,150 L900,400 L920,400 L920,190 L940,190 L940,400 L960,400 L960,130 L980,130 L980,400 L860,400 Z" fill="none" stroke="rgba(255,255,255,0.28)" stroke-width="2"/>
          <!-- 右侧建筑群 -->
          <path d="M1000,400 L1020,400 L1020,240 L1040,240 L1040,400 L1060,400 L1060,170 L1080,170 L1080,400 L1100,400 L1100,210 L1120,210 L1120,400 L1140,400 L1140,260 L1160,260 L1160,400 L1000,400 Z" fill="none" stroke="rgba(255,255,255,0.22)" stroke-width="2"/>
          <!-- 最右侧建筑 -->
          <path d="M1180,400 L1180,300 L1190,300 L1190,400 L1180,400 Z" fill="none" stroke="rgba(255,255,255,0.18)" stroke-width="2"/>
          
          <!-- 建筑窗户装饰（黄色撞色） -->
          <rect x="70" y="330" width="8" height="8" fill="rgba(255,213,79,0.4)"/>
          <rect x="90" y="290" width="8" height="8" fill="rgba(255,213,79,0.4)"/>
          <rect x="130" y="350" width="8" height="8" fill="rgba(255,213,79,0.4)"/>
          <rect x="270" y="260" width="10" height="10" fill="rgba(255,213,79,0.4)"/>
          <rect x="430" y="190" width="10" height="10" fill="rgba(255,213,79,0.4)"/>
          <rect x="610" y="150" width="10" height="10" fill="rgba(255,213,79,0.4)"/>
          <rect x="750" y="110" width="12" height="12" fill="rgba(255,213,79,0.5)"/>
          <rect x="890" y="160" width="10" height="10" fill="rgba(255,213,79,0.4)"/>
          <rect x="1030" y="250" width="8" height="8" fill="rgba(255,213,79,0.4)"/>
          
          <!-- 云朵装饰 -->
          <ellipse cx="100" cy="60" rx="45" ry="22" fill="rgba(255,255,255,0.15)"/>
          <ellipse cx="350" cy="50" rx="55" ry="28" fill="rgba(255,255,255,0.12)"/>
          <ellipse cx="600" cy="70" rx="50" ry="25" fill="rgba(255,255,255,0.15)"/>
          <ellipse cx="850" cy="45" rx="60" ry="30" fill="rgba(255,255,255,0.12)"/>
          <ellipse cx="1050" cy="65" rx="48" ry="24" fill="rgba(255,255,255,0.15)"/>
          
          <!-- 树木装饰 -->
          <path d="M180,400 L180,360 L175,360 L175,375 L170,375 L170,360 L180,360 L180,400 Z" fill="rgba(255,255,255,0.15)"/>
          <circle cx="177.5" cy="350" r="18" fill="rgba(255,255,255,0.15)"/>
          
          <path d="M420,400 L420,370 L415,370 L415,385 L410,385 L410,370 L420,370 L420,400 Z" fill="rgba(255,255,255,0.15)"/>
          <circle cx="417.5" cy="360" r="15" fill="rgba(255,255,255,0.15)"/>
          
          <path d="M680,400 L680,380 L675,380 L675,390 L670,390 L670,380 L680,380 L680,400 Z" fill="rgba(255,255,255,0.15)"/>
          <circle cx="677.5" cy="370" r="12" fill="rgba(255,255,255,0.15)"/>
          
          <path d="M980,400 L980,365 L975,365 L975,380 L970,380 L970,365 L980,365 L980,400 Z" fill="rgba(255,255,255,0.15)"/>
          <circle cx="977.5" cy="355" r="16" fill="rgba(255,255,255,0.15)"/>
        </svg>
      </div>
      
      <!-- 撞色几何图形装饰 -->
      <div class="accent-shapes">
        <!-- 黄色圆形 -->
        <div class="accent-circle accent-circle-1"></div>
        <div class="accent-circle accent-circle-2"></div>
        <!-- 橙色三角形 -->
        <div class="accent-triangle accent-triangle-1"></div>
        <div class="accent-triangle accent-triangle-2"></div>
        <!-- 黄色菱形 -->
        <div class="accent-diamond accent-diamond-1"></div>
      </div>
      
      <!-- 品牌文字 -->
      <div class="brand-text">Boss.</div>
      
      <!-- 背景装饰文字 -->
      <div class="bg-decorative-text">
        <div class="bg-text-item bg-text-1">BOSS直聘</div>
        <div class="bg-text-item bg-text-2">直接谈</div>
        <div class="bg-text-item bg-text-3">找工作</div>
        <div class="bg-text-item bg-text-4">更高效</div>
        <div class="bg-text-item bg-text-5">在线沟通</div>
        <div class="bg-text-item bg-text-6">职位多</div>
      </div>
    </div>
    
    <!-- 顶部客服热线 -->
    <div class="service-hotline">
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M22 16.92V19.92C22 20.52 21.52 21 20.92 21C10.94 21 3 13.06 3 3.08C3 2.48 3.48 2 4.08 2H7.1C7.7 2 8.1 2.48 8.1 3.08C8.1 4.8 8.4 6.5 9 8.1C9.2 8.7 9 9.3 8.5 9.7L6.5 11.7C8.5 15.7 11.3 18.5 15.3 20.5L17.3 18.5C17.7 18 18.3 17.8 18.9 18C20.5 18.6 22.2 18.9 23.92 18.9C24.52 18.9 25 19.3 25 19.9V22.92H22Z" stroke="rgba(255,255,255,0.6)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
      <span>客户服务热线: 400 065 5799</span>
    </div>
    
    <!-- 登录卡片 -->
    <div class="login-card">
      <!-- 切换图标 - 左上角 -->
      <div class="login-mode-toggle" @click="toggleLoginMode" :title="loginMode === 'account' ? '切换到二维码登录' : '切换到账密登录'">
        <svg v-if="loginMode === 'account'" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M3 7V5C3 3.89543 3.89543 3 5 3H7M17 3H19C20.1046 3 21 3.89543 21 5V7M21 17V19C21 20.1046 20.1046 21 19 21H17M7 21H5C3.89543 21 3 20.1046 3 19V17" stroke="#666" stroke-width="2" stroke-linecap="round"/>
          <rect x="7" y="7" width="10" height="10" rx="1" stroke="#666" stroke-width="2"/>
        </svg>
        <svg v-else width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path d="M16 7C16 9.20914 14.2091 11 12 11C9.79086 11 8 9.20914 8 7C8 4.79086 9.79086 3 12 3C14.2091 3 16 4.79086 16 7Z" stroke="#666" stroke-width="2"/>
          <path d="M12 14C8.13401 14 5 17.134 5 21H19C19 17.134 15.866 14 12 14Z" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
        </svg>
      </div>

      <!-- 左右分栏布局 -->
      <div class="login-card-content">
        <!-- 左侧：特色功能展示 -->
        <div class="login-features">
          <div class="feature-header">
            <div class="feature-title">加入BOSS直聘</div>
            <div class="feature-subtitle">开启你的职业新旅程</div>
          </div>
          
          <div class="feature-item">
            <div class="feature-badge">BOSS 直聘</div>
            <div class="feature-text">找工作 上BOSS直聘直接谈</div>
            <div class="feature-desc">与老板直接沟通，跳过HR，快速找到心仪工作</div>
          </div>
          
          <div class="feature-item">
            <div class="feature-icon feature-icon-1">
              <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <!-- 灯泡主体：顶部圆形，下方逐渐变细 -->
                <path d="M12 2C8.13 2 5 5.13 5 9C5 11.38 6.19 13.47 8 14.74V16C8 16.55 8.45 17 9 17H15C15.55 17 16 16.55 16 16V14.74C17.81 13.47 19 11.38 19 9C19 5.13 15.87 2 12 2Z" fill="none" stroke="#9c27b0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                <!-- 灯泡底部螺纹基座 -->
                <path d="M10 17H14C14.55 17 15 17.45 15 18V19C15 19.55 14.55 20 14 20H10C9.45 20 9 19.55 9 19V18C9 17.45 9.45 17 10 17Z" fill="none" stroke="#9c27b0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                <!-- 螺纹线条细节 -->
                <line x1="10.5" y1="18.6" x2="13.5" y2="18.6" stroke="#9c27b0" stroke-width="1.5" stroke-linecap="round"/>
                <line x1="10.5" y1="19.6" x2="13.5" y2="19.6" stroke="#9c27b0" stroke-width="1.5" stroke-linecap="round"/>
              </svg>
            </div>
            <div class="feature-text">在线沟通</div>
            <div class="feature-desc">随时随地与HR或BOSS在线沟通，及时了解职位详情</div>
          </div>
          
          <div class="feature-item">
            <div class="feature-icon feature-icon-2">
              <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M20 7H4C2.9 7 2 7.9 2 9V19C2 20.1 2.9 21 4 21H20C21.1 21 22 20.1 22 19V9C22 7.9 21.1 7 20 7Z" stroke="#9c27b0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                <path d="M16 21V5C16 3.9 15.1 3 14 3H10C8.9 3 8 3.9 8 5V21" stroke="#9c27b0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
              </svg>
            </div>
            <div class="feature-text">职位丰富</div>
            <div class="feature-desc">覆盖互联网、金融、教育等各大行业，职位任你选</div>
          </div>
          
          <div class="feature-stats">
            <div class="stat-item">
              <div class="stat-number">1000万+</div>
              <div class="stat-label">活跃用户</div>
            </div>
            <div class="stat-item">
              <div class="stat-number">500万+</div>
              <div class="stat-label">优质职位</div>
            </div>
          </div>
        </div>

        <!-- 右侧：登录表单 -->
        <div class="login-form-wrapper">
          <!-- APP扫码登录入口 -->
          <div class="app-qrcode-link" @click="loginMode = 'qrcode'" v-if="loginMode === 'account'">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M3 7V5C3 3.89543 3.89543 3 5 3H7M17 3H19C20.1046 3 21 3.89543 21 5V7M21 17V19C21 20.1046 20.1046 21 19 21H17M7 21H5C3.89543 21 3 20.1046 3 19V17" stroke="#666" stroke-width="2" stroke-linecap="round"/>
              <rect x="7" y="7" width="10" height="10" rx="1" stroke="#666" stroke-width="2"/>
            </svg>
            <span>APP扫码登录</span>
          </div>

      <!-- 二维码登录区域 -->
      <div v-if="loginMode === 'qrcode'" class="qrcode-login-area">
        <div class="qrcode-title">钉钉扫码登录</div>
        <div class="qrcode-hint">请使用钉钉APP扫描下方二维码，并在手机上确认登录</div>
        <div class="qrcode-container">
          <!-- 钉钉官方DTFrameLogin组件容器 -->
          <!-- 容器必须一直存在，不能使用v-if，否则DTFrameLogin找不到元素 -->
          <div id="login_container" class="dingtalk-qrcode-container" v-show="qrcodeReady"></div>
          <div v-show="!qrcodeReady" class="qrcode-loading">
            <div class="loading-spinner"></div>
            <div class="loading-text">加载中...</div>
          </div>
          <!-- 刷新按钮 -->
          <button v-if="qrcodeReady" @click="refreshQrcode" class="qrcode-refresh-btn" title="刷新二维码">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M1 4V10H7" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
              <path d="M23 20V14H17" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
              <path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10M23 14L18.36 18.36A9 9 0 0 1 3.51 15" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
          </button>
        </div>
        <button @click="handleDingTalkLogin2" class="dingtalk-login2-btn">钉钉登录2</button>
        <div class="qrcode-tip">
          <a href="#" @click.prevent="loginMode = 'account'" class="switch-to-account">账密登录在这里</a>
        </div>
      </div>

      <!-- 账密登录区域 -->
      <div v-else class="account-login-area">
        <!-- 标题 -->
        <div class="login-form-title">验证码登录/注册</div>
        <div class="login-form-subtitle">首次验证通过即注册BOSS直聘账号</div>
        
        <!-- 角色选择 -->
        <div class="role-tabs">
          <div class="role-tab active">我要找工作</div>
          <div class="role-tab">我要招聘</div>
        </div>
        
        <!-- 登录方式切换 -->
        <div class="login-tabs">
        <div 
          :class="['tab-item', loginType === 'password' ? 'active' : '']"
          @click="loginType = 'password'"
        >
          帐密登录
        </div>
        <div 
          :class="['tab-item', loginType === 'code' ? 'active' : '']"
          @click="loginType = 'code'"
        >
          验证码登录
        </div>
      </div>

      <!-- 登录表单 -->
      <form class="login-form" @submit.prevent="handleLogin">
        <!-- 用户名/手机号/邮箱输入 -->
        <div class="form-group">
          <div class="phone-input-wrapper" v-if="loginType === 'code'">
            <select class="country-code-select">
              <option value="+86">+86</option>
            </select>
            <input
              type="tel"
              v-model="form.phone"
              placeholder="手机号"
              maxlength="11"
              class="form-input phone-input"
              required
            >
          </div>
          <input
            v-else
            type="text"
            v-model="form.phone"
            placeholder="请输入用户名/手机号/邮箱"
            class="form-input"
            required
          >
        </div>

        <!-- 密码/验证码输入 -->
        <div class="form-group">
          <input
            :type="loginType === 'password' ? 'password' : 'text'"
            v-model="currentInputValue"
            :placeholder="loginType === 'password' ? '请输入密码' : '请输入验证码'"
            class="form-input"
            required
          >
          
          <!-- 验证码倒计时按钮 -->
          <button 
            type="button" 
            class="code-btn"
            v-if="loginType === 'code'"
            :disabled="countdown > 0"
            @click="sendCode"
          >
            {{ countdown > 0 ? `${countdown}s后重发` : '获取验证码' }}
          </button>
        </div>

        <!-- 记住密码 -->
        <div class="remember-wrap" v-if="loginType === 'password'">
          <label class="remember-label">
            <input type="checkbox" v-model="form.remember">
            <span>记住密码</span>
          </label>
          <a href="#" class="forget-link">忘记密码？</a>
        </div>

        <!-- 登录按钮 -->
        <button type="submit" class="login-btn" :disabled="loading">
          {{ loading ? '登录中...' : loginType === 'code' ? '登录/注册' : '登录' }}
        </button>
        
        <!-- 用户协议 -->
        <div class="agreement-wrap">
          <label class="agreement-label">
            <input type="checkbox" v-model="agreed" required>
            <span>已阅读并同意BOSS直聘《用户协议》《隐私政策》,允许BOSS直聘统一管理本人账号信息</span>
          </label>
        </div>
        
        <!-- 钉钉扫码登录 -->
        <div class="dingtalk-login" @click="openDingTalkDialog">
          <div class="dingtalk-icon">
            <img src="/static/4535b26e8539308c.png" alt="钉钉登录" />
          </div>
          <span>钉钉扫码登录/注册</span>
        </div>
      </form>

      <!-- 底部信息 -->
      <div class="footer-info">
        <div class="service-info">客服电话 400-065-5799 工作时间:8:00-22:00</div>
        <div class="legal-info">人力资源服务许可证|营业执照|朝阳区人社局监督电话</div>
      </div>
      </div>
      </div>
      </div>
    </div>
    
    <!-- 钉钉扫码登录弹窗 -->
    <el-dialog
      v-model="showDingTalkDialog"
      title="钉钉扫码登录"
      width="400px"
      :close-on-click-modal="false"
      :close-on-press-escape="true"
      @close="closeDingTalkDialog"
    >
      <div class="dingtalk-dialog-content">
        <div class="dingtalk-dialog-hint">请使用钉钉APP扫描下方二维码，并在手机上确认登录</div>
        <div class="dingtalk-dialog-qrcode-container">
          <div id="dingtalk_dialog_container" class="dingtalk-dialog-qrcode" v-show="dialogQrcodeReady"></div>
          <div v-show="!dialogQrcodeReady" class="dingtalk-dialog-loading">
            <div class="loading-spinner"></div>
            <div class="loading-text">正在生成二维码...</div>
          </div>
        </div>
        <div class="dingtalk-dialog-tip">使用钉钉APP扫描二维码完成登录</div>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { login, getDingTalkLoginUrl, sendVerifyCode, codeLogin } from '@/api'
import { useUserStore } from '@/stores/user'
import { ElMessage, ElDialog } from 'element-plus'

export default {
  data() {
    return {
      loginMode: 'account', // 'account' 账密登录, 'qrcode' 二维码登录
      loginType: 'password',
      form: {
        phone: '',
        password: '',
        code: '',
        remember: true
      },
      agreed: false, // 用户协议同意状态
      countdown: 0,
      loading: false,
      qrcodeCheckTimer: null,
      qrcodeReady: false,
      ddLoginInstance: null,
      countdownTimer: null,
      postMessageHandler: null,
      processingCode: null, // 正在处理的授权码，用于防重复提交
      codeProcessedSet: new Set(), // 已处理过的授权码集合，防止重复使用
      showDingTalkDialog: false, // 控制钉钉登录弹窗显示
      dialogQrcodeReady: false, // 弹窗中二维码是否准备好
      dialogQrcodeCheckTimer: null // 弹窗二维码刷新定时器
    }
  },
  mounted() {
    // 如果之前保存了用户名，自动填充
    const savedUsername = localStorage.getItem('savedUsername')
    if (savedUsername) {
      this.form.phone = savedUsername
    }
    
    // 监听postMessage，接收钉钉扫码登录成功的token
    this.postMessageHandler = async (event) => {
      // 验证消息来源（钉钉登录域名）
      if (event.origin && !event.origin.includes('login.dingtalk.com') && !event.origin.includes('dingtalk.com')) {
        console.log('[登录页] 忽略非钉钉来源的消息:', event.origin)
        return
      }
      
      console.log('[登录页] 收到postMessage:', event.data)
      console.log('[登录页] 消息来源:', event.origin)
      
      // 处理对象格式的钉钉登录成功消息
      if (event.data && typeof event.data === 'object' && event.data.type === 'dingtalk_login_success') {
        console.log('[登录页] 收到钉钉登录成功消息（对象格式）')
        const { token, username, signature } = event.data
        if (token) {
          // 调用统一方法处理登录成功流程
          this.handleLoginSuccessAndRedirect(token, username, signature)
        }
        return
      }
      
      // 处理字符串格式的消息（可能是URL或code）
      if (event.data && typeof event.data === 'string' && event.data.trim() !== '') {
        const messageData = event.data.trim()
        
        // 情况1：若返回的是完整URL（包含http/https），提取code
        if (messageData.includes('http://') || messageData.includes('https://')) {
          console.log('[登录页] 收到URL格式的消息，尝试提取code:', messageData.substring(0, 100))
          try {
            // 提取URL中的code参数
            const urlObj = new URL(messageData)
            const code = urlObj.searchParams.get('code')
            if (code) {
              console.log('[登录页] ✓ 从URL中提取到code:', code.substring(0, 20) + '...')
              // 调用后端接口完成登录
              await this.sendCodeToBackend(code)
            } else {
              console.warn('[登录页] ⚠ URL中未找到code参数')
              ElMessage.warning('未获取到授权码，请重试')
            }
          } catch (error) {
            console.error('[登录页] ✗ 解析URL失败:', error)
            // 如果URL解析失败，尝试手动提取code参数
            const codeMatch = messageData.match(/[?&]code=([^&]+)/)
            if (codeMatch && codeMatch[1]) {
              const code = decodeURIComponent(codeMatch[1])
              console.log('[登录页] ✓ 手动提取到code:', code.substring(0, 20) + '...')
              await this.sendCodeToBackend(code)
            } else {
              ElMessage.error('无法从返回数据中提取授权码')
            }
          }
          return
        }
        
        // 情况2：若返回的是直接的code（临时授权码），直接使用
        // 判断可能是code的特征：长度较长、包含字母数字、不是URL
        if (messageData.length > 20 && !messageData.includes(' ')) {
          console.log('[登录页] 收到可能是code的字符串，直接使用:', messageData.substring(0, 20) + '...')
          await this.sendCodeToBackend(messageData)
          return
        }
        
        // 其他字符串格式的消息（可能是旧版签名，已废弃）
        console.log('[登录页] 收到字符串格式的消息，但后端已直接返回完整信息，忽略此消息')
      }
      
      // 处理钉钉登录错误消息
      if (event.data && typeof event.data === 'object' && event.data.type === 'dingtalk_login_error') {
        console.log('[登录页] 收到钉钉登录失败消息:', event.data)
        const { error, message } = event.data
        this.handleDingTalkError(error, event.data.code, message)
      }
    }
    
    // 添加postMessage监听器
    window.addEventListener('message', this.postMessageHandler)
    
    // 检查URL参数（使用window.location更可靠，不依赖Vue Router状态）
    const urlParams = new URLSearchParams(window.location.search)
    
    // 处理钉钉回调的code参数（如果回调直接到前端 /login-success，会携带code参数）
    const code = urlParams.get('code')
    if (code) {
      console.log('[登录页] 检测到URL中的code参数，开始处理钉钉登录回调')
      console.log('[登录页] code:', code.substring(0, 20) + '...')
      // 调用后端接口处理code
      this.sendCodeToBackend(code).then(() => {
        // 处理成功后清除URL参数
        window.history.replaceState({}, document.title, window.location.pathname)
      }).catch((error) => {
        console.error('[登录页] 处理code失败:', error)
        // 错误已在 sendCodeToBackend 中处理，这里只清除URL参数
        window.history.replaceState({}, document.title, window.location.pathname)
      })
      return // 如果处理了code，提前返回，不继续处理其他参数
    }
    
    // 先处理登录成功回调（如果有token参数）- 保留兼容性
    // 直接调用方法，mounted 钩子执行时 methods 已经初始化
    if (this.handleDingTalkCallback && this.handleDingTalkCallback()) {
      return // 如果处理了登录成功，提前返回，不继续处理错误
    }
    
    // 处理错误信息（从钉钉回调返回的错误）
    // 重要修复：只有当用户在二维码登录模式时，才显示钉钉相关的错误提示
    // 如果是通过钉钉回调跳转回来的（带有错误参数），也应该显示错误
    // 但如果用户当前不在二维码登录模式，可能是之前的错误参数残留，则延迟到切换模式时再显示
    const error = urlParams.get('error')
    const errorCode = urlParams.get('code')
    const errorMsg = urlParams.get('msg')
    
    // 钉钉相关的错误类型列表
    const dingtalkErrorTypes = ['dingtalk_failed', 'token_failed', 'code_failed', 'persistent_failed', 'userinfo_failed', 'config_error', 'network_error', 'missing_code', 'missing_unionid', 'token_generate_failed']
    
    // 处理错误信息（从钉钉回调返回的错误）
    if (error) {
      // 如果是钉钉相关错误
      if (dingtalkErrorTypes.includes(error)) {
        // 如果用户当前在二维码登录模式，立即显示错误（可能是刚刚扫码回调的结果）
        if (this.loginMode === 'qrcode') {
          this.handleDingTalkError(error, errorCode, errorMsg)
          // 清除URL中的错误参数
          window.history.replaceState({}, document.title, window.location.pathname)
        } else {
          // 如果用户当前不在二维码登录模式，保留URL参数，不显示错误
          // 错误会在用户切换到二维码登录模式时，由 watch 中的逻辑处理并显示
          console.log('[登录页] 检测到钉钉相关错误参数，但当前不在二维码登录模式，保留URL参数，待切换到二维码模式时再显示')
          // 不清除URL参数，保留给 watch 处理
        }
        return
      }
      
      // 其他非钉钉相关的错误（如果有的话）
      const decodedMsg = errorMsg ? decodeURIComponent(errorMsg) : '登录失败，请重试'
      ElMessage.error(decodedMsg)
      // 清除URL中的错误参数
      window.history.replaceState({}, document.title, window.location.pathname)
    }
  },
  beforeUnmount() {
    // 移除postMessage监听器，防止内存泄漏
    if (this.postMessageHandler) {
      window.removeEventListener('message', this.postMessageHandler)
      this.postMessageHandler = null
      console.log('[登录页] postMessage监听器已移除')
    }
    // 清除定时器
    if (this.qrcodeCheckTimer) {
      clearInterval(this.qrcodeCheckTimer)
      this.qrcodeCheckTimer = null
    }
    // 清除验证码倒计时定时器
    if (this.countdownTimer) {
      clearInterval(this.countdownTimer)
      this.countdownTimer = null
    }
    // 清除DDLogin实例
    this.destroyDdLogin()
  },
  watch: {
    loginMode(newMode) {
      if (newMode === 'qrcode') {
        // 切换到二维码登录模式时，检查URL中是否有钉钉相关错误参数需要处理
        // 这样可以在用户切换到二维码登录时，显示之前可能发生的错误
        this.$nextTick(() => {
          const urlParams = new URLSearchParams(window.location.search)
          const error = urlParams.get('error')
          const errorCode = urlParams.get('code')
          const errorMsg = urlParams.get('msg')
          
          // 如果有钉钉相关错误，现在显示（因为用户已经切换到二维码登录模式）
          if (error && ['dingtalk_failed', 'token_failed', 'code_failed', 'persistent_failed', 'userinfo_failed', 'config_error', 'network_error', 'missing_code', 'missing_unionid', 'token_generate_failed'].includes(error)) {
            this.handleDingTalkError(error, errorCode, errorMsg)
            // 清除URL参数
            window.history.replaceState({}, document.title, window.location.pathname)
          }
          
          // 等待DOM更新后再初始化DDLogin
          setTimeout(() => {
        this.loadDingTalkQrcode()
          }, 100)
        })
      } else {
        // 清除定时器和二维码
        if (this.qrcodeCheckTimer) {
          clearInterval(this.qrcodeCheckTimer)
          this.qrcodeCheckTimer = null
        }
        this.qrcodeReady = false
        this.destroyDdLogin()
      }
    },
    // 监听路由变化，处理钉钉登录回调
    '$route'(to, from) {
      console.log('[路由变化] 从', from.path, '到', to.path)
      // 无论跳转到哪个路径，都统一检查回调参数（包括 /login 和 /login-success）
      // 这样可以确保所有路由都能处理登录成功回调
      this.handleDingTalkCallback()
    }
  },
  computed: {
    currentInputValue: {
      get() {
        return this.loginType === 'password' ? this.form.password : this.form.code
      },
      set(val) {
        if (this.loginType === 'password') {
          this.form.password = val
        } else {
          this.form.code = val
        }
      }
    }
  },
  methods: {
    /**
     * 统一的发送code到后端接口的方法
     * @param {string} code - 钉钉临时授权码
     */
    async sendCodeToBackend(code) {
      if (!code || !code.trim()) {
        console.error('[钉钉登录] ✗ code为空')
        ElMessage.error('授权码为空，请重新扫码')
        return
      }
      
      const trimmedCode = code.trim()
      
      // 防重复提交：如果正在处理相同的code，直接返回
      if (this.processingCode === trimmedCode) {
        console.warn('[钉钉登录] 该授权码正在处理中，忽略重复请求')
        return
      }
      
      // 防重复提交：如果该code已经处理过，直接返回
      if (this.codeProcessedSet.has(trimmedCode)) {
        console.warn('[钉钉登录] 该授权码已处理过，忽略重复请求')
        ElMessage.warning('该授权码已使用，请重新扫码')
        return
      }
      
      // 标记为正在处理
      this.processingCode = trimmedCode
      
      // 记录code接收时间（用于跟踪授权码有效期，目标在10分钟内完成处理）
      const codeReceiveTime = new Date().toISOString()
      console.log('[钉钉登录] code接收时间:', codeReceiveTime)
      
      try {
        console.log('[钉钉登录] ========== 开始调用后端接口 ==========')
        console.log('[钉钉登录] code:', trimmedCode.substring(0, 20) + '...')
        console.log('[钉钉登录] 目标处理时间窗口: 10分钟')
        ElMessage.info('正在登录...')
        
        // 使用统一的API baseURL（从axios配置中获取）
        const baseURL = 'http://localhost:8000'
        const apiUrl = `${baseURL}/dingtalk/handle-code/`
        
        console.log('[钉钉登录] 请求URL:', apiUrl)
        
        const response = await fetch(apiUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ code: trimmedCode })
        })
        
        console.log('[钉钉登录] 响应状态:', response.status, response.statusText)
        
        // 检查HTTP状态码
        if (!response.ok) {
          // 尝试解析错误响应
          let errorMsg = `HTTP错误: ${response.status}`
          let errorCode = null
          let errorData = null
          try {
            errorData = await response.json()
            errorMsg = errorData.message || errorData.error || errorMsg
            errorCode = errorData.code || null
            console.error('[钉钉登录] 错误响应:', errorData)
            
            // 特殊处理：授权码不存在或已过期（40078、invalid_code）
            if (errorData.error_type === 'invalid_code' || 
                errorMsg.includes('40078') || 
                errorMsg.includes('不存在的临时授权码') || 
                errorMsg.includes('临时授权码') ||
                errorMsg.includes('授权码无效') ||
                errorMsg.includes('授权码已过期')) {
              // 标记该code已处理（虽然失败了），防止重复尝试
              this.codeProcessedSet.add(trimmedCode)
              // 清除正在处理标记
              this.processingCode = null
              // 提示用户重新扫码
              ElMessage.error('授权码已过期或无效，请重新扫码登录')
              throw new Error('授权码已过期，请重新扫码')
            }
            
            // 特殊处理：500 服务器内部错误
            if (response.status === 500) {
              // 提供更详细的错误信息
              const serverError = errorData.detail || errorData.error || errorData.message || '服务器内部错误'
              console.error('[钉钉登录] 服务器内部错误详情:', serverError)
              errorMsg = `服务器错误: ${serverError}`
            }
          } catch (e) {
            if (e.message === '授权码已过期，请重新扫码') {
              throw e // 重新抛出授权码过期错误
            }
            console.error('[钉钉登录] 无法解析错误响应:', e)
            // 如果无法解析JSON，使用HTTP状态码作为错误信息
            if (response.status === 500) {
              errorMsg = '服务器内部错误，请稍后重试或联系管理员'
            }
          }
          
          // 清除正在处理标记（错误情况下）
          this.processingCode = null
          throw new Error(errorMsg)
        }
        
        // 解析响应数据
        let data
        try {
          const responseText = await response.text()
          console.log('[钉钉登录] 响应原始数据:', responseText.substring(0, 200))
          data = JSON.parse(responseText)
        } catch (parseError) {
          console.error('[钉钉登录] ✗ JSON解析失败:', parseError)
          throw new Error('服务器返回数据格式错误，请稍后重试')
        }
        
        console.log('[钉钉登录] 后端返回数据:', data)
        
        // 验证响应格式
        if (!data || typeof data !== 'object') {
          throw new Error('后端返回数据格式异常')
        }
        
        // 检查业务状态码
        if (data.code === 200) {
          // 验证data字段存在
          if (!data.data || typeof data.data !== 'object') {
            throw new Error('后端返回数据格式异常：缺少data字段')
          }
          
          // 验证token字段存在且非空
          if (!data.data.token || typeof data.data.token !== 'string' || !data.data.token.trim()) {
            console.error('[钉钉登录] ✗ token字段缺失或为空')
            console.error('[钉钉登录] data.data:', data.data)
            throw new Error('后端返回数据格式异常：token为空或无效')
          }
          
          const token = data.data.token.trim()
          const username = data.data.username || ''
          const openid = data.data.openid || ''
          const signature = data.data.signature || openid || ''
          
          console.log('[钉钉登录] ✓ 提取到token:', token.substring(0, 50) + '...')
          console.log('[钉钉登录] ✓ 提取到username:', username)
          console.log('[钉钉登录] ✓ 提取到openid:', openid ? openid.substring(0, 20) + '...' : '无')
          
          // 存储token到localStorage（关键步骤）
          try {
            localStorage.setItem('token', token)
            console.log('[钉钉登录] ✓ token已保存到localStorage')
            
            // 保存用户信息
            if (username) {
              localStorage.setItem('username', username)
              console.log('[钉钉登录] ✓ username已保存')
            }
            if (openid) {
              localStorage.setItem('openid', openid)
              localStorage.setItem('signature', signature || openid)
              console.log('[钉钉登录] ✓ openid已保存')
            }
            
            // 验证token是否成功保存
            const savedToken = localStorage.getItem('token')
            if (!savedToken || savedToken !== token) {
              throw new Error('token保存失败，请检查浏览器本地存储设置')
            }
            console.log('[钉钉登录] ✓ token保存验证通过')
            
          } catch (storageError) {
            console.error('[钉钉登录] ✗ 本地存储失败:', storageError)
            throw new Error('本地存储失败，请启用浏览器本地存储功能')
          }
          
          // 标记该code已成功处理，防止重复使用
          this.codeProcessedSet.add(trimmedCode)
          // 清除正在处理标记
          this.processingCode = null
          
          // 调用统一的登录成功处理方法
          await this.handleLoginSuccessAndRedirect(token, username, signature)
          
          ElMessage.success('登录成功！')
          
          // 延迟跳转，确保所有状态都已更新
          setTimeout(() => {
            console.log('[钉钉登录] 准备跳转到首页')
            const currentPath = window.location.pathname
            if (currentPath === '/login' || currentPath.includes('/login')) {
              // 使用router.push进行跳转（更优雅）
              this.$router.push('/').catch(err => {
                // 如果router跳转失败，使用window.location
                console.warn('[钉钉登录] router跳转失败，使用window.location:', err)
                window.location.href = '/'
              })
            }
          }, 500)
          
        } else {
          // 业务错误
          const errorMsg = data.message || '登录失败'
          console.error('[钉钉登录] ✗ 业务错误:', errorMsg)
          
          // 特殊处理：授权码不存在或已过期
          if (errorMsg.includes('40078') || errorMsg.includes('不存在的临时授权码') || errorMsg.includes('临时授权码')) {
            // 标记该code已处理（虽然失败了），防止重复尝试
            this.codeProcessedSet.add(trimmedCode)
            // 清除正在处理标记
            this.processingCode = null
            // 提示用户重新扫码
            ElMessage.error('授权码已过期或无效，请重新扫码登录')
            throw new Error('授权码已过期，请重新扫码')
          }
          
          // 清除正在处理标记（错误情况下）
          this.processingCode = null
          throw new Error(errorMsg)
        }
      } catch (error) {
        // 如果错误已经在上面处理过（如授权码过期），直接抛出
        if (error.message === '授权码已过期，请重新扫码') {
          throw error
        }
        
        // 清除正在处理标记（错误情况下）
        this.processingCode = null
        
        console.error('[钉钉登录] ✗ 调用后端接口失败:', error)
        console.error('[钉钉登录] 错误详情:', {
          message: error.message,
          stack: error.stack
        })
        
        // 根据错误类型显示不同的提示
        let userMessage = '登录失败'
        if (error.message.includes('网络') || error.message.includes('fetch') || error.message.includes('ECONNREFUSED')) {
          userMessage = '网络连接失败，请检查网络或确保后端服务已启动（http://localhost:8000）'
        } else if (error.message.includes('JSON')) {
          userMessage = '服务器返回数据格式错误，请稍后重试'
        } else if (error.message.includes('存储')) {
          userMessage = '本地存储失败，请启用浏览器本地存储功能'
        } else if (error.message.includes('授权码') || error.message.includes('code') || error.message.includes('40078') || error.message.includes('invalid_code')) {
          userMessage = '授权码已过期或无效，请重新扫码'
          // 标记该code已处理（虽然失败了），防止重复尝试
          this.codeProcessedSet.add(trimmedCode)
          // 建议刷新二维码
          console.log('[钉钉登录] 授权码错误，建议刷新二维码')
          setTimeout(() => {
            this.refreshQrcode()
          }, 2000)
        } else if (error.message.includes('服务器错误') || error.message.includes('服务器内部错误') || error.message.includes('500')) {
          // 500 错误的友好提示
          userMessage = error.message.includes('服务器错误:') 
            ? error.message 
            : '服务器内部错误，请稍后重试。如果问题持续，请联系管理员检查后端服务状态'
        } else {
          userMessage = error.message || '登录失败，请稍后重试'
        }
        
        ElMessage.error(userMessage)
        throw error
      }
    },
    // 处理钉钉登录错误（提取为独立方法，可在多个地方复用）
    handleDingTalkError(error, errorCode, errorMsg) {
      // 解码错误信息
      const decodedMsg = errorMsg ? decodeURIComponent(errorMsg) : '登录失败，请重试'
      
      // 根据错误类型显示不同的提示
      if (error === 'dingtalk_failed') {
        // 钉钉授权失败（用户拒绝、授权过期等）
        if (errorCode === 'access_denied' || errorCode === 'denied') {
          ElMessage.warning('您取消了授权，登录已取消')
        } else if (errorCode === 'expired' || errorCode === 'invalid' || errorCode === '11021' || errorCode === '40063') {
          ElMessage.error('扫码登录失败：授权码已过期，请重新扫码登录')
        } else if (errorCode === 'unauthorized' || errorCode === 'forbidden') {
          ElMessage.error('您没有授权访问权限，请联系管理员')
        } else if (errorCode === 'missing_code') {
          ElMessage.error('缺少授权码参数，请重新扫码登录')
        } else {
          ElMessage.error(decodedMsg || '扫码登录失败，请刷新重试或选择其他登录方式')
        }
      } else if (error === 'token_failed') {
        // 获取访问令牌失败
        if (errorCode === '40014') {
          ElMessage.error('应用配置错误：APP_KEY无效，请联系管理员')
        } else if (errorCode === '40002') {
          ElMessage.error('应用配置错误：APP_SECRET无效，请联系管理员')
        } else if (errorCode === '40001') {
          ElMessage.error('无权访问：应用未被授权，请联系管理员')
        } else {
          ElMessage.error(decodedMsg || '获取访问令牌失败，请检查应用配置')
        }
      } else if (error === 'code_failed' || error === 'persistent_failed') {
        // 获取持久化授权码失败
        if (errorCode === '40063' || errorCode === '11021') {
          ElMessage.error('授权码已过期，请重新扫码登录')
        } else if (errorCode === '40014') {
          ElMessage.error('授权码无效，请重新扫码登录')
        } else {
          ElMessage.error(decodedMsg || '获取授权码失败，请重试')
        }
      } else if (error === 'userinfo_failed') {
        // 获取用户信息失败
        if (errorCode === '40001') {
          ElMessage.error('无权访问：无法获取用户信息，请联系管理员')
        } else if (errorCode === '40014') {
          ElMessage.error('授权已失效，请重新扫码登录')
        } else {
          ElMessage.error(decodedMsg || '获取用户信息失败，请重试')
        }
      } else if (error === 'config_error') {
        // 应用配置错误
        ElMessage.error(decodedMsg || '应用配置错误，请联系管理员')
      } else if (error === 'network_error') {
        // 网络请求失败
        ElMessage.error(decodedMsg || '网络请求失败，请稍后重试')
      } else if (error === 'missing_code') {
        ElMessage.error('缺少授权码参数，请重新扫码登录')
      } else if (error === 'missing_unionid') {
        ElMessage.error('获取用户信息失败：缺少用户唯一标识')
      } else if (error === 'token_generate_failed') {
        // 增强错误日志，记录关键参数
        console.error('[钉钉登录] token生成失败，参数:', { 
          code: errorCode || '未知',
          errorMsg: errorMsg || '未知错误'
        })
        ElMessage.error('生成登录令牌失败，请重试（详情见控制台）')
      } else {
        // 其他错误
        ElMessage.error(decodedMsg || '扫码登录失败，请刷新重试或选择其他登录方式')
      }
    },
    /**
     * 从回调地址获取参数后，保存用户信息并跳转到home页面
     * @param {string} token - 登录token
     * @param {string} username - 用户名
     * @param {string} signature - 签名（可选，如果有则使用签名获取用户信息）
     * @returns {Promise<void>}
     */
    async handleLoginSuccessAndRedirect(token, username, signature = null) {
      try {
        console.log('[登录成功处理] ========== 开始处理登录成功流程 ==========')
        console.log('[登录成功处理] 接收到的token:', token ? token.substring(0, 50) + '...' : '无')
        console.log('[登录成功处理] 接收到的username:', username || '无')
        console.log('[登录成功处理] 接收到的signature:', signature ? signature.substring(0, 20) + '...' : '无')
        
        // token和username可能已经是解码后的（来自postMessage），也可能需要解码（来自URL参数）
        let decodedToken = token
        let decodedUsername = username || ''
        let decodedSignature = signature || null
        
        // 尝试解码（如果是URL编码的）
        try {
          if (token && token.includes('%')) {
            decodedToken = decodeURIComponent(token)
            console.log('[登录成功处理] token已解码')
          }
        } catch (e) {
          console.log('[登录成功处理] token无需解码或解码失败，使用原始值')
          decodedToken = token
        }
        
        if (username) {
          try {
            if (username.includes('%')) {
              decodedUsername = decodeURIComponent(username)
              console.log('[登录成功处理] username已解码')
            }
          } catch (e) {
            console.log('[登录成功处理] username无需解码或解码失败，使用原始值')
            decodedUsername = username
          }
        }
        
        if (signature) {
          try {
            if (signature.includes('%')) {
              decodedSignature = decodeURIComponent(signature)
              console.log('[登录成功处理] signature已解码')
            }
          } catch (e) {
            console.log('[登录成功处理] signature无需解码或解码失败，使用原始值')
            decodedSignature = signature
          }
        }
        
        // 使用原本的方式：保存JWT token到localStorage（不使用signature作为token）
        try {
          localStorage.setItem('token', decodedToken)
          console.log('[登录成功处理] ✓ token（JWT）已保存到localStorage')
        } catch (e) {
          console.error('[登录成功处理] ✗ 本地存储被禁用或存储失败:', e)
          ElMessage.error('请启用浏览器本地存储功能，否则无法登录')
          return
        }
        
        // 将签名作为openid存储到localStorage和用户数据中（方便后端根据签名查找数据）
        if (decodedSignature) {
          try {
            localStorage.setItem('openid', decodedSignature)
            localStorage.setItem('signature', decodedSignature)
            console.log('[登录成功处理] ✓ signature已作为openid保存到localStorage')
          } catch (e) {
            console.warn('[登录成功处理] ⚠ 保存signature/openid到localStorage失败（不影响登录）:', e)
          }
        }
        
        // 保存username到localStorage
        if (decodedUsername) {
          try {
            localStorage.setItem('username', decodedUsername)
            console.log('[登录成功处理] ✓ username已保存到localStorage')
          } catch (e) {
            console.warn('[登录成功处理] ⚠ 保存username到localStorage失败（不影响登录）:', e)
          }
        }
        
        const userStore = useUserStore()
        // 调用setUserInfo确保完整同步（包含openid字段，使用signature作为openid）
        // setUserInfo方法会自动处理token的保存
        userStore.setUserInfo({
          token: decodedToken,
          user_id: null, // 可以从token中解析，暂时设为null
          username: decodedUsername,
          openid: decodedSignature // 将签名作为openid存储
        })
        
        console.log('[登录成功处理] ✓ token和用户信息已保存到store')
        console.log('[登录成功处理] ✓ 准备跳转到首页')
        
        // 显示成功消息
        ElMessage.success('登录成功！正在跳转到首页...')
        
        // 直接跳转到首页，不依赖路由守卫
        window.location.href = '/'
        
        console.log('[登录成功处理] ✓ 已触发跳转到首页')
      } catch (e) {
        console.error('[登录成功处理] ✗ 处理登录信息失败:', e)
        console.error('[登录成功处理] 错误详情:', e.message, e.stack)
        ElMessage.error('登录失败：处理登录信息时出错，详情见控制台')
        // 出错时跳转到登录页
        window.location.href = '/login'
      }
    },
    
    // 处理钉钉登录成功回调（提取为独立方法，可在mounted和watch中复用）
    // 注意：/login-success 路径现在由 login-success.vue 组件处理，这里只处理 /login 路径的回调
    async handleDingTalkCallback() {
      // 检查URL参数（使用window.location更可靠，不依赖Vue Router状态）
      const urlParams = new URLSearchParams(window.location.search)
      // 兼容多种token参数名：token 或 access_token
      const token = urlParams.get('token') || urlParams.get('access_token')
      const username = urlParams.get('username')
      const signature = urlParams.get('signature')
      
      // 使用window.location.pathname更可靠，因为Vue Router可能还没更新
      const currentPath = window.location.pathname
      
      // 如果有token参数，且当前路径是 /login 或 /login-success，处理登录成功
      if (token && (currentPath === '/login' || currentPath === '/login-success')) {
        console.log('[钉钉登录成功回调] 收到token参数，开始处理登录')
        console.log('[钉钉登录成功回调] 当前路径:', currentPath)
        console.log('[钉钉登录成功回调] token参数:', token ? token.substring(0, 20) + '...' : '无')
        console.log('[钉钉登录成功回调] username参数:', username || '无')
        console.log('[钉钉登录成功回调] signature参数:', signature ? signature.substring(0, 20) + '...' : '无')
        
        // 调用统一方法处理登录成功流程（会自动跳转，不需要await）
        this.handleLoginSuccessAndRedirect(token, username, signature)
        return true // 返回true表示已处理登录成功
      }
      
      return false // 返回false表示没有处理登录成功
    },
    async handleLogin() {
      // 账密登录
      if (this.loginType === 'password') {
        if (!this.form.phone) {
          ElMessage.error('请输入用户名/手机号/邮箱')
          return
        }
        if (!this.form.password) {
          ElMessage.error('请输入密码')
          return
        }

        this.loading = true
        try {
          const response = await login({
            username: this.form.phone,
            password: this.form.password
          })

          if (response.code === 200) {
            const { token, signature, user_id, username } = response.data
            
            // 使用原本的方式：保存JWT token到localStorage（不使用signature作为token）
            localStorage.setItem('token', token)
            console.log('[账密登录] ✓ token（JWT）已保存到localStorage')
            
            // 将签名作为openid存储到localStorage和用户数据中（方便后端根据签名查找数据）
            if (signature) {
              localStorage.setItem('openid', signature)
              localStorage.setItem('signature', signature)
              console.log('[账密登录] ✓ signature已作为openid保存到localStorage')
            }
            
            // 保存username到localStorage
            if (username) {
              localStorage.setItem('username', username)
            }
            
            // 后端已经返回完整信息，直接使用
            // 保存用户信息到store（包含openid字段，使用signature作为openid）
            const userStore = useUserStore()
            userStore.setUserInfo({
              token: token,
              user_id: user_id,
              username: username,
              openid: signature // 将签名作为openid存储
            })

            // 如果勾选了记住密码，保存到localStorage
            if (this.form.remember) {
              localStorage.setItem('savedUsername', this.form.phone)
            } else {
              localStorage.removeItem('savedUsername')
            }

            ElMessage.success('登录成功！')
            // 直接跳转到首页，不依赖路由守卫
            window.location.href = '/'
          }
        } catch (error) {
          ElMessage.error(error.message || '登录失败，请检查用户名和密码')
        } finally {
          this.loading = false
        }
      } 
      // 验证码登录
      else {
        // 验证手机号格式
        if (!/^1[3-9]\d{9}$/.test(this.form.phone)) {
          ElMessage.error('请输入正确的手机号')
          return
        }
        // 验证验证码
        if (!this.form.code || !this.form.code.trim()) {
          ElMessage.error('请输入验证码')
          return
        }
        
        this.loading = true
        try {
          console.log('[验证码登录] 登录参数:', {
            type: this.loginType,
            phone: this.form.phone,
            code: this.form.code.trim()
          })
          
          // 调用后端验证码登录接口
          const response = await codeLogin({
            phone: this.form.phone,
            code: this.form.code.trim()
          })
          
          // 检查响应
          if (response.code === 200) {
            console.log('[验证码登录] ✓ 登录成功:', response)
            
            ElMessage.success(response.msg || '登录成功！')
            
            // 保存登录凭证到本地存储
            if (response.data) {
              const { token, user_id, username, signature } = response.data
              
              if (token) {
                localStorage.setItem('token', token)
                console.log('[验证码登录] ✓ token已保存到localStorage')
              }
              
              if (signature) {
                localStorage.setItem('openid', signature)
                localStorage.setItem('signature', signature)
                console.log('[验证码登录] ✓ signature已保存到localStorage')
              }
              
              if (username) {
                localStorage.setItem('username', username)
              }
              
              // 保存用户信息到store
              const userStore = useUserStore()
              userStore.setUserInfo({
                token: token,
                user_id: user_id,
                username: username,
                openid: signature
              })
            } else {
              console.warn('[验证码登录] ⚠ 响应中未包含用户数据')
            }
            
            // 跳转到首页
            setTimeout(() => {
              window.location.href = '/'
            }, 500)
          } else {
            // 后端返回错误
            ElMessage.error(response.msg || '登录失败，请重试')
          }
        } catch (error) {
          console.error('[验证码登录] 登录失败:', error)
          
          // 处理各种错误情况
          let errorMessage = '登录失败，请稍后重试'
          
          if (error.response && error.response.data) {
            const errorData = error.response.data
            errorMessage = errorData.msg || errorData.message || errorMessage
            
            // 特殊处理：验证码错误或过期
            if (errorMessage.includes('验证码错误') || errorMessage.includes('验证码已过期')) {
              // 验证码错误时，不清除输入，让用户重新输入
              // 但可以提示用户重新获取验证码
              if (errorMessage.includes('验证码已过期')) {
                errorMessage = '验证码已过期，请重新获取验证码'
                // 可以自动清除验证码输入框
                this.form.code = ''
              }
            } else if (errorMessage.includes('参数不完整')) {
              errorMessage = '请填写完整的手机号和验证码'
            }
          } else if (error.message) {
            errorMessage = error.message
          }
          
          ElMessage.error(errorMessage)
        } finally {
          this.loading = false
        }
      }
    },
    async sendCode() {
      // 验证手机号格式
      if (!/^1[3-9]\d{9}$/.test(this.form.phone)) {
        ElMessage.error('请输入正确的手机号')
        return
      }
      
      // 如果正在倒计时，不允许重复发送
      if (this.countdown > 0) {
        ElMessage.warning('请等待倒计时结束后再试')
        return
      }
      
      try {
        console.log('[验证码登录] 发送验证码到:', this.form.phone)
        
        // 调用后端接口发送验证码
        const response = await sendVerifyCode({ phone: this.form.phone })
        
        // 检查响应
        if (response.code === 200) {
          ElMessage.success(response.msg || '验证码发送成功')
          
          // 清除之前的定时器
          if (this.countdownTimer) {
            clearInterval(this.countdownTimer)
          }
          
          // 开始倒计时（60秒）
          this.countdown = 60
          this.countdownTimer = setInterval(() => {
            this.countdown--
            if (this.countdown <= 0) {
              clearInterval(this.countdownTimer)
              this.countdownTimer = null
            }
          }, 1000)
        } else {
          // 后端返回错误
          ElMessage.error(response.msg || '验证码发送失败')
        }
      } catch (error) {
        console.error('[验证码登录] 发送验证码失败:', error)
        
        // 处理各种错误情况
        let errorMessage = '验证码发送失败，请稍后重试'
        
        if (error.response && error.response.data) {
          const errorData = error.response.data
          errorMessage = errorData.msg || errorData.message || errorMessage
          
          // 特殊处理：发送过于频繁
          if (errorMessage.includes('频繁') || errorMessage.includes('1分钟')) {
            errorMessage = '发送过于频繁，请1分钟后再试'
            // 即使发送失败，也设置一个较短的倒计时（30秒）
            this.countdown = 30
            if (this.countdownTimer) {
              clearInterval(this.countdownTimer)
            }
            this.countdownTimer = setInterval(() => {
              this.countdown--
              if (this.countdown <= 0) {
                clearInterval(this.countdownTimer)
                this.countdownTimer = null
              }
            }, 1000)
          }
        } else if (error.message) {
          errorMessage = error.message
        }
        
        ElMessage.error(errorMessage)
      }
    },
    // 切换登录模式
    toggleLoginMode() {
      this.loginMode = this.loginMode === 'account' ? 'qrcode' : 'account'
    },
    // 加载钉钉登录二维码（使用DTFrameLogin - 钉钉新版JSSDK）
    async loadDingTalkQrcode() {
      try {
        // 验证DTFrameLogin脚本是否已加载
        // 注意：钉钉官方推荐使用 DTFrameLogin（新版），但也可以使用 DDLogin（旧版）
        // 如果使用 DDLogin，请引入：https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js
        // 如果使用 DTFrameLogin，请引入：https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js
        if (typeof window.DTFrameLogin === 'undefined' && typeof window.DDLogin === 'undefined') {
          console.error('[钉钉登录] 钉钉登录脚本未加载！')
          console.error('[钉钉登录] 请先引入钉钉登录脚本：')
          console.error('[钉钉登录] - 新版（推荐）：https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js')
          console.error('[钉钉登录] - 旧版：https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js')
          ElMessage.error('钉钉登录组件未加载，请刷新页面重试')
          return
        }
        
        // 重置状态
        this.qrcodeReady = false
        this.destroyDdLogin()
        
        // 获取钉钉登录链接（后端返回appid和回调地址）
        const response = await getDingTalkLoginUrl()
        
        // 验证响应格式
        if (!response || !response.success) {
          console.error('[钉钉登录] 获取配置失败:', response)
          ElMessage.error(response?.error || '获取登录链接失败')
          return
        }
        
        // 解析后端返回的参数（适配现有后端返回格式）
        // 后端应返回：{ success: true, appid: 'xxx', redirect_uri: 'xxx' }
        const appid = response.appid
        const redirectUri = response.redirect_uri || response.redirectUri
        
        if (!appid || !redirectUri) {
          console.error('[钉钉登录] 登录参数不完整:', { appid, redirectUri })
          ElMessage.error('登录参数不完整（缺少appid或redirect_uri）')
          return
        }
        
        console.log('[钉钉登录] 获取到配置:', {
          appid: appid.substring(0, 20) + '...',
          redirect_uri: redirectUri
        })
        
        // 验证二维码容器是否存在（带重试机制）
        let loginContainer = document.getElementById('login_container')
        if (!loginContainer) {
          // 等待DOM更新后重试
          await this.$nextTick()
          await this.$nextTick()
          loginContainer = document.getElementById('login_container')
          if (!loginContainer) {
            console.error('[钉钉登录] 未找到二维码容器 #login_container')
            ElMessage.error('未找到二维码容器，请刷新页面重试')
            return
          }
        }
        
        // 等待DOM更新
        await this.$nextTick()
        await this.$nextTick()
        
        // 优先使用新版 DTFrameLogin，如果不存在则使用旧版 DDLogin
        if (typeof window.DTFrameLogin !== 'undefined') {
          console.log('[钉钉登录] 使用DTFrameLogin（新版）初始化')
          this.initDTFrameLogin(appid, redirectUri)
        } else if (typeof window.DDLogin !== 'undefined') {
          console.log('[钉钉登录] 使用DDLogin（旧版）初始化')
          // 注意：DDLogin 需要 goto 参数（完整授权URL），而不是 redirect_uri
          // 需要构建完整的授权URL
          const gotoUrl = `https://oapi.dingtalk.com/connect/qrconnect?appid=${appid}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${encodeURIComponent(redirectUri)}`
          // 钉钉要求goto参数需要二次编码
          const encodedGoto = encodeURIComponent(gotoUrl)
          this.initDdLoginComponent_OLD(appid, encodedGoto)
        } else {
          throw new Error('钉钉登录组件未加载')
        }
        
      } catch (error) {
        console.error('[钉钉登录] 加载钉钉二维码失败:', error)
        console.error('[钉钉登录] 错误堆栈:', error.stack)
        ElMessage.error(`加载登录二维码失败：${error.message || '未知错误'}，请重试`)
      }
    },
    // 初始化DTFrameLogin组件（钉钉新版JSSDK）
    // clientId: 钉钉应用AppKey（client_id）
    // redirectUri: 回调地址（redirect_uri）
    initDTFrameLogin(clientId, redirectUri) {
      try {
        // 验证参数
        if (!clientId || !clientId.trim()) {
          console.error('[钉钉登录] client_id缺失')
          ElMessage.error('应用ID缺失，无法初始化登录组件')
          return
        }
        
        if (!redirectUri || !redirectUri.trim()) {
          console.error('[钉钉登录] redirect_uri缺失')
          ElMessage.error('回调地址缺失，无法初始化登录组件')
          return
        }
        
        // 检查容器元素
        const container = document.getElementById('login_container')
        if (!container) {
          console.error('[钉钉登录] 找不到容器元素 login_container')
          ElMessage.error('初始化二维码失败：找不到容器元素')
          return
        }
        
        // 清空容器
        container.innerHTML = ''
        
        console.log('[钉钉登录] 使用DTFrameLogin初始化二维码')
        console.log('[钉钉登录] client_id:', clientId)
        console.log('[钉钉登录] redirect_uri:', redirectUri)
        
        // 调用DTFrameLogin生成二维码
        window.DTFrameLogin(
          {
            id: 'login_container',
            width: 300,
            height: 300,
          },
          {
            redirect_uri: encodeURIComponent(redirectUri),
            client_id: clientId,
            scope: 'openid',
            response_type: 'code',
            state: 'STATE',
            prompt: 'consent',
          },
          async (loginResult) => {
            // 登录成功回调
            console.log('[钉钉登录] ✓ 登录成功回调')
            console.log('[钉钉登录] 回调数据:', loginResult)
            const { redirectUrl, authCode } = loginResult
            
            if (authCode) {
              console.log('[钉钉登录] 收到authCode（临时授权码）:', authCode)
              // 使用统一的sendCodeToBackend方法处理
              await this.sendCodeToBackend(authCode)
            } else if (redirectUrl) {
              console.log('[钉钉登录] 收到redirectUrl，但未收到authCode，可能需要等待后端处理')
            } else {
              console.warn('[钉钉登录] 回调数据中既没有authCode也没有redirectUrl')
              ElMessage.warning('登录回调数据异常，请重试')
            }
          },
          (errorMsg) => {
            // 登录失败回调
            console.error('[钉钉登录] ✗ 登录失败:', errorMsg)
            ElMessage.error(`钉钉登录失败: ${errorMsg || '未知错误'}`)
          }
        )
        
        // DTFrameLogin会直接在容器中生成二维码，等待一会儿显示
        setTimeout(() => {
          this.qrcodeReady = true
          console.log('[钉钉登录] ✓ 二维码已生成')
          
          // 设置二维码自动刷新（钉钉二维码有效期约2分钟，建议每90秒刷新一次）
          // 注意：临时授权码有效期约5分钟，但我们在10分钟内处理以确保安全
          if (this.qrcodeCheckTimer) {
            clearInterval(this.qrcodeCheckTimer)
            this.qrcodeCheckTimer = null
          }
          
          // 90秒后自动刷新二维码（在过期前刷新）
          this.qrcodeCheckTimer = setInterval(() => {
            console.log('[钉钉登录] 二维码即将过期，自动刷新...')
            ElMessage.info('二维码即将过期，正在刷新...')
            // 在刷新前清除当前定时器，避免重复
            if (this.qrcodeCheckTimer) {
              clearInterval(this.qrcodeCheckTimer)
              this.qrcodeCheckTimer = null
            }
            // 重新加载二维码（内部会重新设置定时器）
            this.loadDingTalkQrcode()
          }, 90 * 1000) // 90秒
        }, 500)
        
      } catch (error) {
        console.error('[钉钉登录] 初始化DTFrameLogin失败:', error)
        console.error('[钉钉登录] 错误堆栈:', error.stack)
        ElMessage.error(`初始化二维码失败: ${error.message || '未知错误'}，请查看控制台`)
      }
    },
    // 初始化DDLogin组件（旧版，已废弃，保留以备兼容）
    // appid: 钉钉应用ID（必需）
    // redirectUri: 回调地址（redirect_uri），DDLogin会自动构建授权URL
    initDdLoginComponent_OLD(appid, redirectUri) {
      try {
        // 验证参数
        if (!appid || !appid.trim()) {
          console.error('[钉钉登录] appid缺失')
          ElMessage.error('应用ID缺失，无法初始化登录组件')
          return
        }
        
        if (!redirectUri || !redirectUri.trim()) {
          console.error('[钉钉登录] redirect_uri缺失')
          ElMessage.error('回调地址缺失，无法初始化登录组件')
          return
        }
        
        // 检查容器元素
        const container = document.getElementById('login_container')
        if (!container) {
          console.error('[钉钉登录] 找不到容器元素 login_container')
          ElMessage.error('初始化二维码失败：找不到容器元素')
          return
        }
        
        // 清空容器
        container.innerHTML = ''
        
        console.log('[钉钉登录] 使用钉钉官方标准方式初始化DDLogin组件')
        console.log('[钉钉登录] appid:', appid)
        console.log('[钉钉登录] redirect_uri:', redirectUri)
        
        // 根据钉钉官方文档，DDLogin组件的goto参数应该是回调地址（redirect_uri），需要编码
        // DDLogin会自动构建授权URL并生成二维码
        // 注意：根据钉钉官方规范，构建包含appid的完整授权URL作为goto参数
        // 完整授权URL格式：https://oapi.dingtalk.com/connect/qrconnect?appid=xxx&response_type=code&scope=snsapi_login&redirect_uri=xxx
        const authUrl = `https://oapi.dingtalk.com/connect/qrconnect?appid=${appid}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${encodeURIComponent(redirectUri)}`
        
        const config = {
          id: "login_container",
          goto: encodeURIComponent(authUrl),  // 完整的授权URL（编码后），符合钉钉官方规范
          style: "border:none;background-color:#FFFFFF;",
          width: "365",
          height: "400"
        }
        
        console.log('[钉钉登录] DDLogin配置:', {
          ...config,
          goto: config.goto.substring(0, 50) + '...'
        })
        
        // 初始化DDLogin组件（钉钉官方标准方式）
        try {
          this.ddLoginInstance = window.DDLogin(config)
          console.log('[钉钉登录] ✓ DDLogin实例创建成功')
        } catch (error) {
          console.error('[钉钉登录] ✗ DDLogin初始化失败:', error)
          ElMessage.error('钉钉登录组件初始化失败，请检查控制台日志')
          throw error
        }
        
        // 检查iframe是否创建
        let checkCount = 0
        const maxChecks = 10
        const checkInterval = 500
        
        const checkIframe = () => {
          const iframe = container.querySelector('iframe')
          checkCount++
          
          if (iframe) {
            console.log('[钉钉登录] ✓ 二维码iframe已加载')
            console.log('[钉钉登录] iframe src:', iframe.src)
            console.log('[钉钉登录] iframe尺寸:', iframe.offsetWidth, 'x', iframe.offsetHeight)
            // 初始化成功后显示容器
            this.qrcodeReady = true
            console.log('[钉钉登录] 钉钉登录二维码加载成功，请用钉钉APP扫码并在手机上确认登录')
            
            // 监听iframe的加载事件
            iframe.onload = () => {
              console.log('[钉钉登录] ✓ iframe内容加载完成')
              console.log('[钉钉登录] iframe最终src:', iframe.src)
              
              // 检查iframe URL中是否包含token（登录成功回调）
              try {
                const iframeUrl = new URL(iframe.src)
                const token = iframeUrl.searchParams.get('token') || iframeUrl.searchParams.get('access_token')
                if (token) {
                  console.log('[钉钉登录] ✓ 检测到iframe URL中包含token，通知父页面处理跳转')
                  // 通知父页面处理跳转（避免iframe内跨域限制）
                  window.parent.postMessage({ 
                    type: 'dingtalk_login_success', 
                    token: token,
                    username: iframeUrl.searchParams.get('username')
                  }, '*')
                }
              } catch (e) {
                // 忽略跨域错误（正常情况）
                console.log('[钉钉登录] 无法访问iframe URL（跨域限制，正常）')
              }
              
              // 检查iframe尺寸（如果为0，可能是错误页面）
              const iframeWidth = iframe.offsetWidth || iframe.clientWidth
              const iframeHeight = iframe.offsetHeight || iframe.clientHeight
              console.log('[钉钉登录] iframe实际尺寸:', iframeWidth, 'x', iframeHeight)
              
              if (iframeWidth === 0 || iframeHeight === 0) {
                console.warn('[钉钉登录] ⚠ 警告：iframe尺寸为0，可能是错误页面')
                // 延迟再次检查，因为内容可能还在加载
                setTimeout(() => {
                  const retryWidth = iframe.offsetWidth || iframe.clientWidth
                  const retryHeight = iframe.offsetHeight || iframe.clientHeight
                  if (retryWidth === 0 || retryHeight === 0) {
                    console.error('[钉钉登录] ✗ 错误：iframe尺寸仍为0，钉钉登录页面可能返回了错误')
                    console.error('[钉钉登录] 可能原因：1) appid无效或不存在 2) 应用未在钉钉开放平台授权 3) 回调地址配置错误 4) 网络错误')
                    console.error('[钉钉登录] ========== 问题诊断建议 ==========')
                    console.error('[钉钉登录] 1. 检查钉钉开放平台（https://open.dingtalk.com）中的应用配置')
                    console.error('[钉钉登录] 2. 确认APP_KEY是否正确（当前appid:', appid, ')')
                    console.error('[钉钉登录] 3. 检查回调地址是否在钉钉开放平台中正确配置')
                    console.error('[钉钉登录] 4. 确认应用已通过审核并已授权给目标企业')
                    console.error('[钉钉登录] 5. 检查应用权限范围是否包含"登录"权限')
                    console.error('[钉钉登录] ========================================')
                    ElMessage.error({
                      message: '二维码加载异常。可能原因：1) 应用ID无效或不存在 2) 应用未在钉钉开放平台授权 3) 回调地址配置错误。请查看控制台获取详细诊断信息。',
                      duration: 8000,
                      showClose: true
                    })
                  }
                }, 2000)
              }
              
              // 检查iframe URL是否包含错误参数
              try {
                const iframeUrl = new URL(iframe.src)
                const error = iframeUrl.searchParams.get('error')
                const errorMsg = iframeUrl.searchParams.get('error_description') || iframeUrl.searchParams.get('msg')
                
                if (error) {
                  console.error('[钉钉登录] ✗ 钉钉登录页面返回错误:', error, errorMsg)
                  let userMsg = '钉钉登录失败'
                  
                  if (error === 'invalid_appid' || error === 'appid_not_found' || error === 'missing_appid' || errorMsg?.toLowerCase().includes('缺少appid') || errorMsg?.toLowerCase().includes('appid缺失')) {
                    userMsg = '应用ID无效、不存在或缺失。请检查：1) 后端APP_KEY配置是否正确 2) 后端是否返回了appid字段 3) appid是否正确传递到DDLogin组件 4) 应用是否在钉钉开放平台存在'
                    console.error('[钉钉登录] ✗ 应用ID无效、不存在或缺失')
                    console.error('[钉钉登录] 当前使用的appid:', appid)
                    console.error('[钉钉登录] appid类型:', typeof appid)
                    console.error('[钉钉登录] appid长度:', appid ? appid.length : 0)
                    console.error('[钉钉登录] 错误详情:', errorMsg)
                    console.error('[钉钉登录] 请前往 https://open.dingtalk.com 检查应用配置')
                  } else if (error === 'unauthorized' || error === 'forbidden') {
                    userMsg = '应用未授权。请检查：1) 应用是否已通过审核 2) 应用是否已授权给目标企业 3) 企业是否已同意应用授权'
                    console.error('[钉钉登录] ✗ 应用未授权，当前appid:', appid)
                    console.error('[钉钉登录] 请前往钉钉开放平台确认应用授权状态')
                  } else if (error === 'access_denied') {
                    userMsg = '访问被拒绝。请检查：1) 应用权限范围是否包含"登录"权限 2) 应用是否已正确配置回调地址'
                    console.error('[钉钉登录] ✗ 访问被拒绝，当前appid:', appid)
                  } else if (errorMsg) {
                    userMsg = `钉钉登录错误：${decodeURIComponent(errorMsg)}`
                    console.error('[钉钉登录] ✗ 钉钉返回错误:', error, decodeURIComponent(errorMsg))
                  }
                  
                  ElMessage.error({
                    message: userMsg,
                    duration: 8000,
                    showClose: true
                  })
                }
              } catch (e) {
                // URL解析失败，忽略
              }
              
              // 检查iframe是否可以访问（跨域时会失败）
              try {
                const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document
                if (iframeDoc) {
                  console.log('[钉钉登录] ✓ iframe可以访问（同域）')
                  // 如果可以访问，检查页面内容
                  const bodyText = iframeDoc.body?.innerText || ''
                  // 检测各种错误信息
                  const bodyTextLower = bodyText.toLowerCase()
                  if (bodyTextLower.includes('appid') && (bodyTextLower.includes('不存在') || bodyTextLower.includes('无效') || bodyTextLower.includes('缺少'))) {
                    console.error('[钉钉登录] ✗ 检测到错误信息：appid不存在、无效或缺少')
                    console.error('[钉钉登录] 当前使用的appid:', appid)
                    console.error('[钉钉登录] iframe内容:', bodyText)
                    ElMessage.error({
                      message: '应用ID不存在、无效或缺少。请检查：1) 后端APP_KEY配置是否正确 2) 后端是否返回了appid字段 3) appid是否正确传递到DDLogin组件',
                      duration: 10000,
                      showClose: true
                    })
                  }
                  if (bodyTextLower.includes('权限') || bodyTextLower.includes('授权') || bodyTextLower.includes('没有权限')) {
                    console.error('[钉钉登录] ✗ 检测到权限错误')
                    console.error('[钉钉登录] 当前使用的appid:', appid)
                    ElMessage.error({
                      message: '应用未授权或没有权限。请检查：1) 应用是否已在钉钉开放平台授权 2) 应用是否已通过审核 3) APP_KEY是否正确',
                      duration: 10000,
                      showClose: true
                    })
                  }
                  if (bodyTextLower.includes('缺少appid') || bodyTextLower.includes('appid缺失')) {
                    console.error('[钉钉登录] ✗ 检测到：缺少appid')
                    console.error('[钉钉登录] 当前传递给DDLogin的appid:', appid)
                    console.error('[钉钉登录] DDLogin配置中是否有appid:', this.ddLoginInstance ? '需检查' : '实例未创建')
                    ElMessage.error({
                      message: '检测到缺少appid错误。这可能是因为：1) appid未正确传递到DDLogin组件 2) 后端未返回appid字段。请查看控制台日志获取详细信息。',
                      duration: 10000,
                      showClose: true
                    })
                  }
                }
              } catch (e) {
                console.log('[钉钉登录] ⚠ iframe跨域，无法直接访问（这是正常的）')
              }
            }
            iframe.onerror = (error) => {
              console.error('[钉钉登录] ✗ iframe加载错误:', error)
              ElMessage.error('二维码加载失败，请检查网络连接或刷新页面重试')
            }
            
            // 监听iframe URL变化（检测错误）
            let lastSrc = iframe.src
            const urlCheckInterval = setInterval(() => {
              if (iframe.src !== lastSrc) {
                console.log('[钉钉登录] iframe URL已变化:', iframe.src)
                lastSrc = iframe.src
                
                // 检查新URL是否包含错误
                try {
                  const urlObj = new URL(iframe.src)
                  const error = urlObj.searchParams.get('error')
                  if (error) {
                    clearInterval(urlCheckInterval)
                    const errorMsg = urlObj.searchParams.get('error_description') || urlObj.searchParams.get('msg')
                    console.error('[钉钉登录] ✗ URL变化检测到错误:', error, errorMsg)
                    
                    if (error === 'invalid_appid' || error === 'appid_not_found' || error === 'missing_appid' || errorMsg?.toLowerCase().includes('缺少appid') || errorMsg?.toLowerCase().includes('appid缺失')) {
                      console.error('[钉钉登录] ✗ URL变化检测到：应用ID无效、不存在或缺失')
                      console.error('[钉钉登录] 当前使用的appid:', appid)
                      console.error('[钉钉登录] appid类型:', typeof appid)
                      console.error('[钉钉登录] appid长度:', appid ? appid.length : 0)
                      console.error('[钉钉登录] 错误信息:', errorMsg)
                      ElMessage.error({
                        message: '应用ID无效、不存在或缺失。请检查：1) 后端APP_KEY配置是否正确 2) 后端是否返回了appid字段 3) appid是否正确传递到DDLogin组件 4) 应用是否在钉钉开放平台存在',
                        duration: 10000,
                        showClose: true
                      })
                    } else if (error === 'unauthorized' || error === 'forbidden') {
                      console.error('[钉钉登录] ✗ URL变化检测到：应用未授权，当前appid:', appid)
                      ElMessage.error({
                        message: '应用未授权。请检查：1) 应用是否已通过审核 2) 应用是否已授权给目标企业',
                        duration: 8000,
                        showClose: true
                      })
                    } else if (errorMsg) {
                      console.error('[钉钉登录] ✗ URL变化检测到错误:', error, decodeURIComponent(errorMsg))
                      ElMessage.error({
                        message: `钉钉登录错误：${decodeURIComponent(errorMsg)}`,
                        duration: 8000,
                        showClose: true
                      })
                    }
                  }
                } catch (e) {
                  // URL解析失败，忽略
                }
              }
            }, 1000)
            
            // 保存定时器以便清理
            this._iframeUrlCheckInterval = urlCheckInterval
            
            // 监听钉钉登录回调消息（钉钉会通过postMessage发送登录结果，或iframe通知父页面）
            const messageHandler = async (event) => {
              console.log('[钉钉登录] 收到postMessage消息:', event)
              console.log('[钉钉登录] 消息来源:', event.origin)
              console.log('[钉钉登录] 消息数据:', event.data)
              
              // 处理对象格式的iframe通知的登录成功消息
              if (event.data && typeof event.data === 'object' && event.data.type === 'dingtalk_login_success') {
                console.log('[钉钉登录] ✓ 收到iframe通知的登录成功消息（对象格式）')
                const token = event.data.token
                const username = event.data.username
                const signature = event.data.signature
                if (token) {
                  this.handleLoginSuccessAndRedirect(token, username, signature)
                  return
                }
              }
              
              // 处理字符串格式的消息（可能是签名）
              // 注意：后端回调会直接返回完整信息，不需要通过签名获取用户信息
              if (event.data && typeof event.data === 'string' && event.data.trim() !== '') {
                console.log('[钉钉登录] 收到字符串格式的消息，但后端已直接返回完整信息，忽略此消息')
                return
              }
              
              // 钉钉登录成功后会跳转到回调地址，不会通过postMessage
              // 但如果收到消息，说明有交互发生
              if (event.data && typeof event.data === 'object') {
                console.log('[钉钉登录] 消息详情:', JSON.stringify(event.data, null, 2))
              }
            }
            window.addEventListener('message', messageHandler)
            
            // 保存监听器，以便后续清理
            this._dingtalkMessageHandler = messageHandler
            
            // 监听页面可见性变化，检测是否跳转到回调页面
            const visibilityHandler = () => {
              if (document.hidden) {
                console.log('[钉钉登录] 页面隐藏，可能正在跳转...')
              } else {
                console.log('[钉钉登录] 页面显示')
              }
            }
            document.addEventListener('visibilitychange', visibilityHandler)
            this._visibilityHandler = visibilityHandler
          } else if (checkCount < maxChecks) {
            console.log(`[钉钉登录] 等待iframe加载... (${checkCount}/${maxChecks})`)
            setTimeout(checkIframe, checkInterval)
          } else {
            console.warn('[钉钉登录] 警告：容器内未找到iframe，二维码可能未正确加载')
            console.warn('[钉钉登录] 容器内容:', container.innerHTML)
            // 即使没有iframe也显示，可能是延迟加载
            this.qrcodeReady = true
          }
        }
        
        // 开始检查iframe
        setTimeout(checkIframe, 500)
        
        // 设置二维码自动刷新（钉钉二维码有效期约2分钟，建议每90秒刷新一次）
        // 注意：临时授权码有效期约5分钟，但我们在10分钟内处理以确保安全
        // 优化刷新逻辑：清除之前的定时器，确保定时器正确清除和重启
        if (this.qrcodeCheckTimer) {
          clearInterval(this.qrcodeCheckTimer)
          this.qrcodeCheckTimer = null
        }
        
        // 90秒后自动刷新二维码（在过期前刷新）
        this.qrcodeCheckTimer = setInterval(() => {
          console.log('[钉钉登录] 二维码即将过期，自动刷新...')
          ElMessage.info('二维码即将过期，正在刷新...')
          // 在刷新前清除当前定时器，避免重复
          if (this.qrcodeCheckTimer) {
            clearInterval(this.qrcodeCheckTimer)
            this.qrcodeCheckTimer = null
          }
          // 重新加载二维码（内部会重新设置定时器）
          this.loadDingTalkQrcode()
        }, 90 * 1000) // 90秒
        
      } catch (error) {
        console.error('[钉钉登录] 初始化DDLogin失败:', error)
        console.error('[钉钉登录] 错误堆栈:', error.stack)
        ElMessage.error(`初始化二维码失败: ${error.message || '未知错误'}，请查看控制台`)
      }
    },
    // 销毁钉钉登录实例
    destroyDdLogin() {
      // 清理消息监听器
      if (this._dingtalkMessageHandler) {
        window.removeEventListener('message', this._dingtalkMessageHandler)
        this._dingtalkMessageHandler = null
      }
      
      // 清理可见性监听器
      if (this._visibilityHandler) {
        document.removeEventListener('visibilitychange', this._visibilityHandler)
        this._visibilityHandler = null
      }
      
      // 清理iframe URL检查定时器
      if (this._iframeUrlCheckInterval) {
        clearInterval(this._iframeUrlCheckInterval)
        this._iframeUrlCheckInterval = null
      }
      
      // 清理二维码刷新定时器
      if (this.qrcodeCheckTimer) {
        clearInterval(this.qrcodeCheckTimer)
        this.qrcodeCheckTimer = null
      }
      
      // 清空容器（DTFrameLogin生成的二维码会被移除）
      const container = document.getElementById('login_container')
      if (container) {
        container.innerHTML = ''
      }
      
      // 清理实例引用
      this.ddLoginInstance = null
    },
    // 刷新二维码
    refreshQrcode() {
      console.log('[钉钉登录] 手动刷新二维码')
      // 清除旧的定时器
      if (this.qrcodeCheckTimer) {
        clearInterval(this.qrcodeCheckTimer)
        this.qrcodeCheckTimer = null
      }
      // 重新加载二维码
      this.loadDingTalkQrcode()
      // 注意：loadDingTalkQrcode 内部会重新设置定时器，无需在这里重复设置
    },
    // 钉钉登录2按钮点击处理
    handleDingTalkLogin2() {
<<<<<<< HEAD
      window.location.href = 'https://oapi.dingtalk.com/connect/qrconnect?appid=dinggwwrjpslthefuf8u&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=http%3A//localhost%3A5173/login-success'
=======
      const loginUrl = 'https://oapi.dingtalk.com/connect/qrconnect?appid=dinggwwrjpslthefuf8u&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=http%3A//localhost%3A5173/login-success'
      window.location.href = loginUrl
    },
    // 打开钉钉登录弹窗
    async openDingTalkDialog() {
      this.showDingTalkDialog = true
      // 等待弹窗DOM渲染完成
      await this.$nextTick()
      // 加载二维码
      await this.loadDingTalkQrcodeForDialog()
    },
    // 关闭钉钉登录弹窗
    closeDingTalkDialog() {
      this.showDingTalkDialog = false
      // 清理弹窗中的二维码
      this.destroyDialogQrcode()
    },
    // 在弹窗中加载钉钉登录二维码
    async loadDingTalkQrcodeForDialog() {
      try {
        // 验证DTFrameLogin脚本是否已加载
        if (typeof window.DTFrameLogin === 'undefined' && typeof window.DDLogin === 'undefined') {
          console.error('[钉钉登录] 钉钉登录脚本未加载！')
          ElMessage.error('钉钉登录组件未加载，请刷新页面重试')
          return
        }
        
        // 重置状态
        this.dialogQrcodeReady = false
        this.destroyDialogQrcode()
        
        // 获取钉钉登录链接（后端返回appid和回调地址）
        const response = await getDingTalkLoginUrl()
        
        // 验证响应格式
        if (!response || !response.success) {
          console.error('[钉钉登录] 获取配置失败:', response)
          ElMessage.error(response?.error || '获取登录链接失败')
          return
        }
        
        // 解析后端返回的参数
        const appid = response.appid
        const redirectUri = response.redirect_uri || response.redirectUri
        
        if (!appid || !redirectUri) {
          console.error('[钉钉登录] 登录参数不完整:', { appid, redirectUri })
          ElMessage.error('登录参数不完整（缺少appid或redirect_uri）')
          return
        }
        
        // 验证二维码容器是否存在
        const dialogContainer = document.getElementById('dingtalk_dialog_container')
        if (!dialogContainer) {
          console.error('[钉钉登录] 未找到弹窗二维码容器 #dingtalk_dialog_container')
          ElMessage.error('未找到二维码容器')
          return
        }
        
        // 等待DOM更新
        await this.$nextTick()
        
        // 优先使用新版 DTFrameLogin，如果不存在则使用旧版 DDLogin
        if (typeof window.DTFrameLogin !== 'undefined') {
          console.log('[钉钉登录] 使用DTFrameLogin（新版）初始化弹窗二维码')
          this.initDialogDTFrameLogin(appid, redirectUri)
        } else if (typeof window.DDLogin !== 'undefined') {
          console.log('[钉钉登录] 使用DDLogin（旧版）初始化弹窗二维码')
          const gotoUrl = `https://oapi.dingtalk.com/connect/qrconnect?appid=${appid}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${encodeURIComponent(redirectUri)}`
          const encodedGoto = encodeURIComponent(gotoUrl)
          this.initDialogDdLoginComponent(appid, encodedGoto)
        } else {
          throw new Error('钉钉登录组件未加载')
        }
        
      } catch (error) {
        console.error('[钉钉登录] 加载弹窗二维码失败:', error)
        ElMessage.error(`加载登录二维码失败：${error.message || '未知错误'}，请重试`)
      }
    },
    // 初始化弹窗中的DTFrameLogin组件
    initDialogDTFrameLogin(clientId, redirectUri) {
      try {
        if (!clientId || !clientId.trim()) {
          ElMessage.error('应用ID缺失，无法初始化登录组件')
          return
        }
        
        if (!redirectUri || !redirectUri.trim()) {
          ElMessage.error('回调地址缺失，无法初始化登录组件')
          return
        }
        
        const container = document.getElementById('dingtalk_dialog_container')
        if (!container) {
          ElMessage.error('初始化二维码失败：找不到容器元素')
          return
        }
        
        // 清空容器
        container.innerHTML = ''
        
        // 调用DTFrameLogin生成二维码
        window.DTFrameLogin(
          {
            id: 'dingtalk_dialog_container',
            width: 300,
            height: 300,
          },
          {
            redirect_uri: encodeURIComponent(redirectUri),
            client_id: clientId,
            scope: 'openid',
            response_type: 'code',
            state: 'STATE',
            prompt: 'consent',
          },
          async (loginResult) => {
            // 登录成功回调
            console.log('[钉钉登录] ✓ 弹窗登录成功回调')
            console.log('[钉钉登录] 弹窗回调数据:', loginResult)
            const { redirectUrl, authCode, state } = loginResult
            
            if (authCode) {
              console.log('[钉钉登录] 弹窗收到authCode（临时授权码）:', authCode)
              // 使用统一的sendCodeToBackend方法处理
              await this.sendCodeToBackend(authCode)
            } else if (redirectUrl) {
              console.log('[钉钉登录] 弹窗收到redirectUrl，但未收到authCode，可能需要等待后端处理')
            } else {
              console.warn('[钉钉登录] 弹窗回调数据中既没有authCode也没有redirectUrl')
              ElMessage.warning('登录回调数据异常，请重试')
            }
          },
          (errorMsg) => {
            // 登录失败回调
            console.error('[钉钉登录] ✗ 弹窗登录失败:', errorMsg)
            ElMessage.error(`钉钉登录失败: ${errorMsg || '未知错误'}`)
          }
        )
        
        // 等待二维码生成
        setTimeout(() => {
          this.dialogQrcodeReady = true
          console.log('[钉钉登录] ✓ 弹窗二维码已生成')
          
          // 设置二维码自动刷新
          if (this.dialogQrcodeCheckTimer) {
            clearInterval(this.dialogQrcodeCheckTimer)
            this.dialogQrcodeCheckTimer = null
          }
          
          // 90秒后自动刷新二维码
          this.dialogQrcodeCheckTimer = setInterval(() => {
            console.log('[钉钉登录] 弹窗二维码即将过期，自动刷新...')
            ElMessage.info('二维码即将过期，正在刷新...')
            
            if (this.dialogQrcodeCheckTimer) {
              clearInterval(this.dialogQrcodeCheckTimer)
              this.dialogQrcodeCheckTimer = null
            }
            
            this.loadDingTalkQrcodeForDialog()
          }, 90000)
        }, 1000)
        
      } catch (error) {
        console.error('[钉钉登录] 初始化弹窗DTFrameLogin失败:', error)
        ElMessage.error(`初始化二维码失败: ${error.message || '未知错误'}，请查看控制台`)
      }
    },
    // 初始化弹窗中的DDLogin组件（旧版）
    initDialogDdLoginComponent(appid, goto) {
      try {
        if (!appid || !appid.trim()) {
          ElMessage.error('应用ID缺失，无法初始化登录组件')
          return
        }
        
        const container = document.getElementById('dingtalk_dialog_container')
        if (!container) {
          ElMessage.error('初始化二维码失败：找不到容器元素')
          return
        }
        
        // 清空容器
        container.innerHTML = ''
        
        // 初始化DDLogin组件
        const config = {
          id: 'dingtalk_dialog_container',
          goto: goto,
          style: 'border:none;background-color:#FFFFFF;',
          width: '300',
          height: '300'
        }
        
        window.DDLogin(config)
        
        // 等待iframe加载
        setTimeout(() => {
          this.dialogQrcodeReady = true
          console.log('[钉钉登录] 弹窗钉钉登录二维码加载成功')
          
          // 设置二维码自动刷新
          if (this.dialogQrcodeCheckTimer) {
            clearInterval(this.dialogQrcodeCheckTimer)
            this.dialogQrcodeCheckTimer = null
          }
          
          this.dialogQrcodeCheckTimer = setInterval(() => {
            console.log('[钉钉登录] 弹窗二维码即将过期，自动刷新...')
            ElMessage.info('二维码即将过期，正在刷新...')
            
            if (this.dialogQrcodeCheckTimer) {
              clearInterval(this.dialogQrcodeCheckTimer)
              this.dialogQrcodeCheckTimer = null
            }
            
            this.loadDingTalkQrcodeForDialog()
          }, 90000)
        }, 2000)
        
      } catch (error) {
        console.error('[钉钉登录] 初始化弹窗DDLogin失败:', error)
        ElMessage.error(`初始化二维码失败: ${error.message || '未知错误'}，请查看控制台`)
      }
    },
    // 清理弹窗中的二维码
    destroyDialogQrcode() {
      // 清理二维码刷新定时器
      if (this.dialogQrcodeCheckTimer) {
        clearInterval(this.dialogQrcodeCheckTimer)
        this.dialogQrcodeCheckTimer = null
      }
      
      // 清空容器
      const container = document.getElementById('dingtalk_dialog_container')
      if (container) {
        container.innerHTML = ''
      }
      
      this.dialogQrcodeReady = false
>>>>>>> login
    }
  }
}
</script>

<style scoped>
.login-container {
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

/* 背景样式 - 梦幻紫渐变 */
.login-bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, #e1bee7 0%, #ce93d8 15%, #ba68c8 30%, #ab47bc 50%, #9c27b0 70%, #8e24aa 85%, #7b1fa2 100%);
  background-size: cover;
  overflow: hidden;
}

/* 城市建筑轮廓 */
.city-skyline {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 65%;
  z-index: 1;
  opacity: 0.7;
}

.city-svg {
  width: 100%;
  height: 100%;
}

/* 撞色几何图形装饰 */
.accent-shapes {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
}

.accent-circle {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 213, 79, 0.25);
  animation: float 6s ease-in-out infinite;
}

.accent-circle-1 {
  width: 120px;
  height: 120px;
  top: 15%;
  left: 12%;
  animation-delay: 0s;
}

.accent-circle-2 {
  width: 80px;
  height: 80px;
  top: 65%;
  right: 15%;
  background: rgba(255, 152, 0, 0.3);
  animation-delay: 2s;
}

.accent-triangle {
  position: absolute;
  width: 0;
  height: 0;
}

.accent-triangle-1 {
  border-left: 60px solid transparent;
  border-right: 60px solid transparent;
  border-bottom: 100px solid rgba(255, 213, 79, 0.2);
  top: 30%;
  right: 20%;
  animation-delay: 1s;
}

.accent-triangle-2 {
  border-left: 40px solid transparent;
  border-right: 40px solid transparent;
  border-bottom: 70px solid rgba(255, 152, 0, 0.25);
  top: 55%;
  left: 20%;
  animation-delay: 3s;
}

.accent-diamond {
  position: absolute;
  width: 70px;
  height: 70px;
  background: rgba(255, 213, 79, 0.3);
}

.accent-diamond-1 {
  top: 40%;
  left: 60%;
  animation-delay: 1.5s;
}

@keyframes float {
  0%, 100% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(-20px);
  }
}

@keyframes float-rotate-triangle1 {
  0%, 100% {
    transform: translateY(0px) rotate(15deg);
  }
  50% {
    transform: translateY(-20px) rotate(20deg);
  }
}

@keyframes float-rotate-triangle2 {
  0%, 100% {
    transform: translateY(0px) rotate(-20deg);
  }
  50% {
    transform: translateY(-20px) rotate(-15deg);
  }
}

@keyframes float-rotate-diamond {
  0%, 100% {
    transform: translateY(0px) rotate(45deg);
  }
  50% {
    transform: translateY(-20px) rotate(50deg);
  }
}

.accent-triangle-1 {
  animation: float-rotate-triangle1 8s ease-in-out infinite;
}

.accent-triangle-2 {
  animation: float-rotate-triangle2 8s ease-in-out infinite;
}

.accent-diamond-1 {
  animation: float-rotate-diamond 7s ease-in-out infinite;
}

/* 品牌文字 */
.brand-text {
  position: absolute;
  top: 15%;
  left: 8%;
  font-size: 64px;
  font-weight: 300;
  color: rgba(255, 255, 255, 0.4);
  letter-spacing: 4px;
  z-index: 3;
  font-family: 'Arial', sans-serif;
}

/* 背景装饰文字 */
.bg-decorative-text {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  pointer-events: none;
}

.bg-text-item {
  position: absolute;
  font-size: 72px;
  font-weight: 200;
  color: rgba(255, 255, 255, 0.08);
  letter-spacing: 8px;
  white-space: nowrap;
  font-family: 'Arial', sans-serif;
  transform: rotate(-15deg);
}

.bg-text-1 {
  top: 25%;
  left: 5%;
  font-size: 80px;
}

.bg-text-2 {
  top: 45%;
  left: 15%;
  font-size: 68px;
  transform: rotate(12deg);
}

.bg-text-3 {
  top: 35%;
  right: 20%;
  font-size: 76px;
  transform: rotate(-8deg);
}

.bg-text-4 {
  top: 60%;
  left: 25%;
  font-size: 70px;
  transform: rotate(10deg);
}

.bg-text-5 {
  top: 50%;
  right: 10%;
  font-size: 74px;
  transform: rotate(-12deg);
}

.bg-text-6 {
  top: 70%;
  right: 25%;
  font-size: 66px;
  transform: rotate(8deg);
}

/* 顶部客服热线 */
.service-hotline {
  position: absolute;
  top: 30px;
  right: 30px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: rgba(255, 255, 255, 0.8);
  font-size: 14px;
  z-index: 20;
}

.service-hotline svg {
  width: 16px;
  height: 16px;
}

/* 登录卡片 */
.login-card {
  position: absolute;
  right: 8%;
  top: 50%;
  transform: translateY(-50%);
  width: 900px;
  height: 650px;
  padding: 0;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
  z-index: 10;
  display: flex;
  flex-direction: column;
  overflow: hidden; /* 防止内容溢出 */
}

/* 登录卡片内容区域 */
.login-card-content {
  display: flex;
  flex: 1;
  height: 100%;
}

/* 左侧特色功能区域 - 占1/3 */
.login-features {
  flex: 0 0 300px;
  padding: 50px 35px;
  background: linear-gradient(135deg, #f3e5f5 0%, #ffffff 100%);
  border-right: 1px solid rgba(156, 39, 176, 0.15);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 30px;
  overflow-y: auto;
}

.feature-header {
  margin-bottom: 10px;
}

.feature-title {
  font-size: 24px;
  font-weight: 600;
  color: #333;
  margin-bottom: 8px;
}

.feature-subtitle {
  font-size: 14px;
  color: #666;
  line-height: 1.5;
}

.feature-item {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.feature-desc {
  font-size: 13px;
  color: #999;
  line-height: 1.6;
  margin-top: 4px;
}

.feature-stats {
  display: flex;
  gap: 20px;
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid #eee;
}

.stat-item {
  flex: 1;
  text-align: center;
}

.stat-number {
  font-size: 20px;
  font-weight: 600;
  color: #9c27b0;
  margin-bottom: 4px;
}

.stat-label {
  font-size: 12px;
  color: #999;
}

.feature-badge {
  display: inline-block;
  padding: 6px 16px;
  background: linear-gradient(135deg, #9c27b0 0%, #ab47bc 100%);
  color: #fff;
  border-radius: 4px;
  font-size: 14px;
  font-weight: 500;
  width: fit-content;
  box-shadow: 0 2px 8px rgba(156, 39, 176, 0.3);
}

.feature-icon {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(156, 39, 176, 0.12);
  border-radius: 8px;
}

.feature-text {
  font-size: 16px;
  color: #333;
  line-height: 1.6;
}

/* 右侧登录表单区域 - 占2/3 */
.login-form-wrapper {
  flex: 1;
  padding: 40px 50px;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow-y: auto;
  min-width: 0;
}

/* 切换图标 - 左上角 */
.login-mode-toggle {
  position: absolute;
  top: 15px;
  left: 15px;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 8px;
  transition: all 0.3s;
  z-index: 11;
  background: rgba(255, 255, 255, 0.9);
}

.login-mode-toggle:hover {
  background: #f5f5f5;
  transform: scale(1.05);
}

.login-mode-toggle svg {
  width: 20px;
  height: 20px;
  transition: transform 0.3s;
}

.login-mode-toggle:hover svg {
  transform: rotate(90deg);
}

/* APP扫码登录链接 */
.app-qrcode-link {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #666;
  font-size: 14px;
  cursor: pointer;
  margin-bottom: 20px;
  justify-content: flex-end;
  transition: color 0.3s;
}

.app-qrcode-link:hover {
  color: #9c27b0;
}

/* 表单标题 */
.login-form-title {
  font-size: 22px;
  font-weight: 600;
  color: #333;
  margin-bottom: 8px;
}

.login-form-subtitle {
  font-size: 13px;
  color: #999;
  margin-bottom: 24px;
}

/* 角色选择标签 */
.role-tabs {
  display: flex;
  gap: 12px;
  margin-bottom: 30px;
}

.role-tab {
  flex: 1;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f5f5f5;
  border-radius: 8px;
  color: #666;
  font-size: 15px;
  cursor: pointer;
  transition: all 0.3s;
}

.role-tab.active {
  background: #fff;
  color: #9c27b0;
  font-weight: 500;
  box-shadow: 0 2px 8px rgba(156, 39, 176, 0.2);
}

/* 登录选项卡 */
.login-tabs {
  display: flex;
  margin-bottom: 30px;
  border-bottom: 1px solid #eee;
  flex-shrink: 0;
}

.tab-item {
  padding: 0 10px 10px;
  margin-right: 20px;
  color: #666;
  font-size: 16px;
  cursor: pointer;
  position: relative;
}

.tab-item.active {
  color: #9c27b0;
  font-weight: 500;
}

.tab-item.active::after {
  content: '';
  position: absolute;
  bottom: -1px;
  left: 0;
  width: 100%;
  height: 2px;
  background: #9c27b0;
}

/* 登录表单 */
.login-form {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* 表单样式 */
.form-group {
  position: relative;
  margin-bottom: 20px;
  flex-shrink: 0;
}

.form-input {
  width: 100%;
  height: 48px;
  padding: 0 15px;
  border: 1px solid #ddd;
  border-radius: 8px;
  font-size: 15px;
  box-sizing: border-box;
  transition: all 0.3s;
  background: #fafafa;
}

.form-input:focus {
  outline: none;
  border-color: #9c27b0;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(156, 39, 176, 0.15);
}

/* 手机号输入包装器 */
.phone-input-wrapper {
  display: flex;
  gap: 0;
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
  background: #fafafa;
  transition: all 0.3s;
}

.phone-input-wrapper:focus-within {
  border-color: #9c27b0;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(156, 39, 176, 0.15);
}

.country-code-select {
  padding: 0 12px;
  height: 48px;
  border: none;
  background: transparent;
  font-size: 15px;
  color: #333;
  cursor: pointer;
  outline: none;
  border-right: 1px solid #ddd;
}

.phone-input {
  flex: 1;
  border: none;
  background: transparent;
  padding-left: 15px;
}

.phone-input:focus {
  box-shadow: none;
}

/* 验证码按钮 */
.code-btn {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  height: 32px;
  padding: 0 15px;
  background: #f5f5f5;
  border: none;
  border-radius: 4px;
  color: #333;
  font-size: 14px;
  cursor: pointer;
  transition: background 0.3s;
}

.code-btn:disabled {
  background: #eee;
  color: #999;
  cursor: not-allowed;
}

/* 记住密码 */
.remember-wrap {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 30px;
  font-size: 14px;
}

.remember-label {
  display: flex;
  align-items: center;
  color: #666;
  cursor: pointer;
}

.remember-label input {
  margin-right: 5px;
}

.forget-link {
  color: #9c27b0;
  text-decoration: none;
}

.forget-link:hover {
  text-decoration: underline;
}

/* 登录按钮 */
.login-btn {
  width: 100%;
  height: 48px;
  background: linear-gradient(135deg, #9c27b0 0%, #ab47bc 100%);
  border: none;
  border-radius: 8px;
  color: #fff;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s;
  margin-top: 10px;
  box-shadow: 0 4px 12px rgba(156, 39, 176, 0.3);
}

.login-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, #8e24aa 0%, #9c27b0 100%);
  box-shadow: 0 6px 16px rgba(156, 39, 176, 0.4);
  transform: translateY(-2px);
}

.login-btn:disabled {
  background: #ccc;
  cursor: not-allowed;
  transform: none;
}


/* 账密登录区域 */
.account-login-area {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow-y: auto; /* 如果内容过多，允许滚动 */
  min-height: 0; /* 允许flex收缩 */
}

/* 用户协议 */
.agreement-wrap {
  margin: 20px 0;
  flex-shrink: 0;
}

.agreement-label {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 12px;
  color: #666;
  line-height: 1.6;
  cursor: pointer;
}

.agreement-label input[type="checkbox"] {
  margin-top: 2px;
  flex-shrink: 0;
  cursor: pointer;
}

.agreement-label span {
  flex: 1;
}

/* 钉钉扫码登录 */
.dingtalk-login {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #9c27b0;
  font-size: 14px;
  cursor: pointer;
  margin-top: 10px;
  transition: opacity 0.3s;
}

.dingtalk-login:hover {
  opacity: 0.8;
}

.dingtalk-icon {
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.dingtalk-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* 底部信息 */
.footer-info {
  margin-top: auto;
  padding-top: 20px;
  flex-shrink: 0;
}

.service-info {
  font-size: 12px;
  color: #999;
  text-align: center;
  margin-bottom: 8px;
}

.legal-info {
  font-size: 12px;
  color: #ccc;
  text-align: center;
  line-height: 1.6;
}

/* 二维码登录区域 */
.qrcode-login-area {
  text-align: center;
  padding: 10px 0 20px 0;
  display: flex;
  flex-direction: column;
  flex: 1;
  justify-content: flex-start;
  min-height: 0; /* 允许flex收缩 */
  overflow: hidden; /* 防止溢出 */
}

.qrcode-title {
  font-size: 20px;
  font-weight: 600;
  color: #333;
  margin-bottom: 12px;
  flex-shrink: 0;
}

.qrcode-hint {
  font-size: 13px;
  color: #666;
  margin-bottom: 25px;
  line-height: 1.6;
  flex-shrink: 0;
  padding: 0 10px;
}

.qrcode-container {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20px;
  min-height: 300px; /* 最小高度，与login_container一致 */
  flex-shrink: 0; /* 不允许收缩 */
  background: #fafafa;
  border-radius: 8px;
  border: 1px solid #eee;
}

/* DTFrameLogin组件容器样式 */
#login_container,
.dingtalk-qrcode-container {
  width: 300px;
  height: 300px;
  margin: 0 auto;
  position: relative;
}

.qrcode-refresh-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 32px;
  height: 32px;
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid #ddd;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.3s;
  z-index: 10;
}

.qrcode-refresh-btn:hover {
  background: #fff;
  border-color: #9c27b0;
  transform: rotate(90deg);
}

.qrcode-refresh-btn svg {
  width: 16px;
  height: 16px;
}

.qrcode-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #999;
  font-size: 14px;
}

.loading-spinner {
  width: 40px;
  height: 40px;
  border: 3px solid #f3f3f3;
  border-top: 3px solid #9c27b0;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 15px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.loading-text {
  color: #999;
  font-size: 14px;
}

.qrcode-tip {
  margin-top: 15px;
  flex-shrink: 0;
}

.dingtalk-login2-btn {
  width: 100%;
  height: 44px;
  margin-top: 20px;
  background: linear-gradient(135deg, #9c27b0 0%, #ab47bc 100%);
  border: none;
  border-radius: 8px;
  color: #fff;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.3s;
  flex-shrink: 0;
  box-shadow: 0 4px 12px rgba(156, 39, 176, 0.3);
}

.dingtalk-login2-btn:hover {
  background: linear-gradient(135deg, #8e24aa 0%, #9c27b0 100%);
}

.switch-to-account {
  color: #9c27b0;
  text-decoration: none;
  font-size: 14px;
  cursor: pointer;
}

.switch-to-account:hover {
  text-decoration: underline;
}

/* 响应式调整 */
@media (max-width: 1024px) {
  .login-card {
    width: 95%;
    right: 2.5%;
    height: auto;
    min-height: 600px;
  }
  
  .login-card-content {
    flex-direction: column;
    min-height: 600px;
  }
  
  .login-features {
    flex: 0 0 auto;
    padding: 30px 40px;
    border-right: none;
    border-bottom: 1px solid rgba(156, 39, 176, 0.15);
    flex-direction: row;
    justify-content: space-around;
    gap: 20px;
    max-height: 300px;
    overflow-y: auto;
  }
  
  .feature-item {
    flex: 1;
    text-align: center;
  }
  
  .login-form-wrapper {
    padding: 30px 40px;
    min-height: 400px;
  }
  
  .bg-text-item {
    font-size: 50px;
  }
}

@media (max-width: 768px) {
  .login-card {
    width: 90%;
    right: 5%;
    height: auto;
    min-height: 550px;
  }
  
  .login-card-content {
    min-height: 550px;
  }
  
  .login-features {
    flex-direction: column;
    padding: 20px 30px;
    gap: 25px;
    max-height: none;
  }
  
  .login-form-wrapper {
    padding: 30px 20px;
    min-height: 350px;
  }
  
  .service-hotline {
    top: 15px;
    right: 15px;
    font-size: 12px;
  }
  
  .brand-text {
    font-size: 36px;
  }
  
  .city-skyline {
    height: 50%;
  }
  
  .bg-text-item {
    font-size: 40px;
    letter-spacing: 4px;
  }
}

@media (max-width: 480px) {
  .login-card {
    width: 95%;
    right: 2.5%;
    height: auto;
    min-height: 500px;
  }
  
  .login-features {
    padding: 15px 20px;
  }
  
  .login-form-wrapper {
    padding: 20px 15px;
    min-height: 300px;
  }
  
  .login-form-title {
    font-size: 18px;
  }
  
  .feature-text {
    font-size: 14px;
  }
  
  .service-hotline {
    display: none;
  }
  
  .bg-text-item {
    font-size: 32px;
    letter-spacing: 2px;
  }
  
  .brand-text {
    font-size: 28px;
  }
}

/* 钉钉登录弹窗样式 */
.dingtalk-dialog-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 0;
}

.dingtalk-dialog-hint {
  font-size: 14px;
  color: #666;
  margin-bottom: 20px;
  text-align: center;
  line-height: 1.6;
}

.dingtalk-dialog-qrcode-container {
  position: relative;
  width: 300px;
  height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #f5f5f5;
  border-radius: 8px;
  margin-bottom: 20px;
}

.dingtalk-dialog-qrcode {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.dingtalk-dialog-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.loading-spinner {
  width: 40px;
  height: 40px;
  border: 3px solid #f3f3f3;
  border-top: 3px solid #0088FF;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.loading-text {
  font-size: 14px;
  color: #999;
}

.dingtalk-dialog-tip {
  font-size: 12px;
  color: #999;
  text-align: center;
  line-height: 1.6;
}
</style>
